001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdi.internal.connect;
011:
012: import java.io.IOException;
013: import java.io.InterruptedIOException;
014: import java.util.ArrayList;
015: import java.util.LinkedList;
016: import java.util.ListIterator;
017:
018: import org.eclipse.jdi.TimeoutException;
019: import org.eclipse.jdi.internal.VirtualMachineImpl;
020: import org.eclipse.jdi.internal.jdwp.JdwpCommandPacket;
021: import org.eclipse.jdi.internal.jdwp.JdwpPacket;
022: import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket;
023:
024: import com.ibm.icu.text.MessageFormat;
025: import com.sun.jdi.VMDisconnectedException;
026: import com.sun.jdi.connect.spi.Connection;
027:
028: /**
029: * This class implements a thread that receives packets from the Virtual
030: * Machine.
031: *
032: */
033: public class PacketReceiveManager extends PacketManager {
034:
035: /** Generic timeout value for not blocking. */
036: public static final int TIMEOUT_NOT_BLOCKING = 0;
037:
038: /** Generic timeout value for infinite timeout. */
039: public static final int TIMEOUT_INFINITE = -1;
040:
041: /** List of Command packets received from Virtual Machine. */
042: private LinkedList fCommandPackets;
043:
044: /** List of Reply packets received from Virtual Machine. */
045: private LinkedList fReplyPackets;
046:
047: /** List of Packets that have timed out already. Maintained so that responses can be
048: * discarded if/when they are received. */
049: private ArrayList fTimedOutPackets;
050:
051: private VirtualMachineImpl fVM;
052:
053: /**
054: * Create a new thread that receives packets from the Virtual Machine.
055: */
056: public PacketReceiveManager(Connection connection,
057: VirtualMachineImpl vmImpl) {
058: super (connection);
059: fVM = vmImpl;
060: fCommandPackets = new LinkedList();
061: fReplyPackets = new LinkedList();
062: fTimedOutPackets = new ArrayList();
063: }
064:
065: public void disconnectVM() {
066: super .disconnectVM();
067: synchronized (fCommandPackets) {
068: fCommandPackets.notifyAll();
069: }
070: synchronized (fReplyPackets) {
071: fReplyPackets.notifyAll();
072: }
073: }
074:
075: /**
076: * Thread's run method.
077: */
078: public void run() {
079: try {
080: while (!VMIsDisconnected()) {
081: // Read a packet from the input stream.
082: readAvailablePacket();
083: }
084: }
085: //if the remote VM is interrupted, drop the connection and clean up, don't wait for it to happen on its own
086: catch (InterruptedIOException e) {
087: disconnectVM(e);
088: } catch (IOException e) {
089: disconnectVM(e);
090: }
091: }
092:
093: /**
094: * @return Returns a specified Command Packet from the Virtual Machine.
095: */
096: public JdwpCommandPacket getCommand(int command, long timeToWait)
097: throws InterruptedException {
098: JdwpCommandPacket packet = null;
099: synchronized (fCommandPackets) {
100: long remainingTime = timeToWait;
101: long timeBeforeWait;
102: long waitedTime;
103:
104: // Wait until command is available.
105: while (!VMIsDisconnected()
106: && (packet = removeCommandPacket(command)) == null
107: && (timeToWait < 0 || remainingTime > 0)) {
108: timeBeforeWait = System.currentTimeMillis();
109: waitForPacketAvailable(remainingTime, fCommandPackets);
110: waitedTime = System.currentTimeMillis()
111: - timeBeforeWait;
112: remainingTime -= waitedTime;
113: }
114: }
115: // Check for an IO Exception.
116: if (VMIsDisconnected()) {
117: String message;
118: if (getDisconnectException() == null) {
119: message = ConnectMessages.PacketReceiveManager_Got_IOException_from_Virtual_Machine_1;
120: } else {
121: String exMessage = getDisconnectException()
122: .getMessage();
123: if (exMessage == null) {
124: message = MessageFormat
125: .format(
126: ConnectMessages.PacketReceiveManager_Got__0__from_Virtual_Machine_1,
127: new String[] { getDisconnectException()
128: .getClass().getName() });
129: } else {
130: message = MessageFormat
131: .format(
132: ConnectMessages.PacketReceiveManager_Got__0__from_Virtual_Machine___1__1,
133: new String[] {
134: getDisconnectException()
135: .getClass()
136: .getName(),
137: exMessage });
138: }
139: }
140: throw new VMDisconnectedException(message);
141: }
142: // Check for a timeout.
143: if (packet == null) {
144: throw new TimeoutException();
145: }
146: return packet;
147: }
148:
149: /**
150: * @return Returns a specified Reply Packet from the Virtual Machine.
151: */
152: public JdwpReplyPacket getReply(int id, long timeToWait) {
153: JdwpReplyPacket packet = null;
154: long remainingTime = timeToWait;
155: synchronized (fReplyPackets) {
156: final long timeBeforeWait = System.currentTimeMillis();
157: // Wait until reply is available.
158: while (!VMIsDisconnected() && remainingTime > 0) {
159: packet = removeReplyPacket(id);
160: if (packet != null) {
161: break;
162: }
163: try {
164: waitForPacketAvailable(remainingTime, fReplyPackets);
165: }
166: // if the remote VM is interrupted DO NOT drop the connection - see bug 171075
167: // just stop waiting for the reply and treat it as a timeout
168: catch (InterruptedException e) {
169: break;
170: }
171: long waitedTime = System.currentTimeMillis()
172: - timeBeforeWait;
173: remainingTime = timeToWait - waitedTime;
174: }
175: }
176: if (packet == null) {
177: synchronized (fReplyPackets) {
178: packet = removeReplyPacket(id);
179: }
180: }
181: // Check for an IO Exception.
182: if (VMIsDisconnected())
183: throw new VMDisconnectedException(
184: ConnectMessages.PacketReceiveManager_Got_IOException_from_Virtual_Machine_2);
185: // Check for a timeout.
186: if (packet == null) {
187: synchronized (fTimedOutPackets) {
188: fTimedOutPackets.add(new Integer(id));
189: }
190: throw new TimeoutException(MessageFormat.format(
191: ConnectMessages.PacketReceiveManager_0,
192: new String[] { id + "" })); //$NON-NLS-1$
193: }
194: return packet;
195: }
196:
197: /**
198: * @return Returns a specified Reply Packet from the Virtual Machine.
199: */
200: public JdwpReplyPacket getReply(JdwpCommandPacket commandPacket) {
201: return getReply(commandPacket.getId(), fVM.getRequestTimeout());
202: }
203:
204: /**
205: * Wait for an available packet from the Virtual Machine.
206: */
207: private void waitForPacketAvailable(long timeToWait, Object lock)
208: throws InterruptedException {
209: if (timeToWait == 0)
210: return;
211: else if (timeToWait < 0)
212: lock.wait();
213: else
214: lock.wait(timeToWait);
215: }
216:
217: /**
218: * @return Returns and removes a specified command packet from the command
219: * packet list.
220: */
221: private JdwpCommandPacket removeCommandPacket(int command) {
222: ListIterator iter = fCommandPackets.listIterator();
223: while (iter.hasNext()) {
224: JdwpCommandPacket packet = (JdwpCommandPacket) iter.next();
225: if (packet.getCommand() == command) {
226: iter.remove();
227: return packet;
228: }
229: }
230: return null;
231: }
232:
233: /**
234: * @return Returns a specified reply packet from the reply packet list.
235: */
236: private JdwpReplyPacket removeReplyPacket(int id) {
237: ListIterator iter = fReplyPackets.listIterator();
238: while (iter.hasNext()) {
239: JdwpReplyPacket packet = (JdwpReplyPacket) iter.next();
240: if (packet.getId() == id) {
241: iter.remove();
242: return packet;
243: }
244: }
245: return null;
246: }
247:
248: /**
249: * Add a command packet to the command packet list.
250: */
251: private void addCommandPacket(JdwpCommandPacket packet) {
252: if (isTimedOut(packet)) {
253: return; // already timed out. No need to keep this one
254: }
255: synchronized (fCommandPackets) {
256: fCommandPackets.add(packet);
257: fCommandPackets.notifyAll();
258: }
259: }
260:
261: /**
262: * Returns whether the request for the given packet has already timed out.
263: *
264: * @param packet response packet
265: * @return whether the request for the given packet has already timed out
266: */
267: private boolean isTimedOut(JdwpPacket packet) {
268: synchronized (fTimedOutPackets) {
269: if (fTimedOutPackets.isEmpty()) {
270: return false;
271: }
272: Integer id = new Integer(packet.getId());
273: return fTimedOutPackets.remove(id);
274: }
275: }
276:
277: /**
278: * Add a reply packet to the reply packet list.
279: */
280: private void addReplyPacket(JdwpReplyPacket packet) {
281: if (isTimedOut(packet)) {
282: return; // already timed out. No need to keep this one
283: }
284: synchronized (fReplyPackets) {
285: fReplyPackets.add(packet);
286: fReplyPackets.notifyAll();
287: }
288: }
289:
290: /**
291: * Read a packet from the input stream and add it to the appropriate packet
292: * list.
293: */
294: private void readAvailablePacket() throws IOException {
295: // Read a packet from the Input Stream.
296: byte[] bytes = getConnection().readPacket();
297: JdwpPacket packet = JdwpPacket.build(bytes);
298: // Add packet to command or reply queue.
299: if (packet instanceof JdwpCommandPacket)
300: addCommandPacket((JdwpCommandPacket) packet);
301: else
302: addReplyPacket((JdwpReplyPacket) packet);
303: }
304: }
|