001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Ivan G. Popov
021: * @version $Revision: 1.5 $
022: */
023:
024: /**
025: * Created on 05.23.2004
026: */package org.apache.harmony.jpda.tests.framework.jdwp;
027:
028: import java.net.InetAddress;
029: import java.net.ServerSocket;
030: import java.net.Socket;
031: import java.net.SocketTimeoutException;
032: import java.io.InputStream;
033: import java.io.InterruptedIOException;
034: import java.io.OutputStream;
035: import java.io.IOException;
036:
037: import org.apache.harmony.jpda.tests.framework.jdwp.Packet;
038:
039: /**
040: * This class provides TransportWrapper for row TCP/IP socket connection.
041: *
042: */
043: public class SocketTransportWrapper implements TransportWrapper {
044:
045: public static final String HANDSHAKE_STRING = "JDWP-Handshake";
046:
047: private ServerSocket serverSocket;
048: private Socket transportSocket;
049: private InputStream input;
050: private OutputStream output;
051:
052: /**
053: * Starts listening for connection on given or default address.
054: *
055: * @param address address to listen or null for default address
056: * @return string representation of listening address
057: */
058: public String startListening(String address) throws IOException {
059: String hostName = null;
060: InetAddress hostAddr = null;
061: int port = 0;
062: if (address != null) {
063: String portName = null;
064: int i = address.indexOf(':');
065: if (i < 0) {
066: portName = address;
067: } else {
068: hostName = address.substring(0, i);
069: portName = address.substring(i + 1);
070: }
071: try {
072: port = Integer.parseInt(portName);
073: } catch (NumberFormatException e) {
074: throw new IOException(
075: "Illegal port number in socket address: "
076: + address);
077: }
078: }
079:
080: if (hostName != null) {
081: hostAddr = InetAddress.getByName(hostName);
082: serverSocket = new ServerSocket(port, 0, hostAddr);
083: } else {
084: serverSocket = new ServerSocket(port);
085: }
086:
087: // use as workaround for unspecified behaviour of isAnyLocalAddress()
088: InetAddress iAddress = null;
089: if (hostName != null) {
090: iAddress = serverSocket.getInetAddress();
091: } else {
092: iAddress = InetAddress.getLocalHost();
093: }
094:
095: address = iAddress.getHostName() + ":"
096: + serverSocket.getLocalPort();
097: return address;
098: }
099:
100: /**
101: * Stops listening for connection on current address.
102: */
103: public void stopListening() throws IOException {
104: if (serverSocket != null) {
105: serverSocket.close();
106: }
107: }
108:
109: /**
110: * Accepts transport connection for currently listened address and performs handshaking
111: * for specified timeout.
112: *
113: * @param acceptTimeout timeout for accepting in milliseconds
114: * @param handshakeTimeout timeout for handshaking in milliseconds
115: */
116: public void accept(long acceptTimeout, long handshakeTimeout)
117: throws IOException {
118: synchronized (serverSocket) {
119: serverSocket.setSoTimeout((int) acceptTimeout);
120: try {
121: transportSocket = serverSocket.accept();
122: } finally {
123: serverSocket.setSoTimeout(0);
124: }
125: }
126: createStreams();
127: handshake(handshakeTimeout);
128: }
129:
130: /**
131: * Attaches transport connection to given address and performs handshaking
132: * for specified timeout.
133: *
134: * @param address address for attaching
135: * @param attachTimeout timeout for attaching in milliseconds
136: * @param handshakeTimeout timeout for handshaking in milliseconds
137: */
138: public void attach(String address, long attachTimeout,
139: long handshakeTimeout) throws IOException {
140: if (address == null) {
141: throw new IOException("Illegal socket address: " + address);
142: }
143:
144: String hostName = null;
145: int port = 0;
146: {
147: String portName = null;
148: int i = address.indexOf(':');
149: if (i < 0) {
150: throw new IOException("Illegal socket address: "
151: + address);
152: } else {
153: hostName = address.substring(0, i);
154: portName = address.substring(i + 1);
155: }
156: try {
157: port = Integer.parseInt(portName);
158: } catch (NumberFormatException e) {
159: throw new IOException(
160: "Illegal port number in socket address: "
161: + address);
162: }
163: }
164:
165: long finishTime = System.currentTimeMillis() + attachTimeout;
166: long sleepTime = 4 * 1000; // millesecinds
167: IOException exception = null;
168: try {
169: do {
170: try {
171: transportSocket = new Socket(hostName, port);
172: break;
173: } catch (IOException e) {
174: Thread.sleep(sleepTime);
175: }
176: } while (attachTimeout == 0
177: || System.currentTimeMillis() < finishTime);
178: } catch (InterruptedException e) {
179: throw new InterruptedIOException(
180: "Interruption in attaching to " + address);
181: }
182:
183: if (transportSocket == null) {
184: if (exception != null) {
185: throw exception;
186: } else {
187: throw new SocketTimeoutException(
188: "Timeout exceeded in attaching to " + address);
189: }
190: }
191:
192: createStreams();
193: handshake(handshakeTimeout);
194: }
195:
196: /**
197: * Closes transport connection.
198: */
199: public void close() throws IOException {
200: if (input != null) {
201: input.close();
202: }
203: if (output != null) {
204: output.close();
205: }
206:
207: if (transportSocket != null && input == null && output == null
208: && !transportSocket.isClosed()) {
209: transportSocket.close();
210: }
211: if (serverSocket != null) {
212: serverSocket.close();
213: }
214: }
215:
216: /**
217: * Checks if transport connection is open.
218: *
219: * @return true if transport connection is open
220: */
221: public boolean isOpen() {
222: return (transportSocket != null
223: && transportSocket.isConnected() && !transportSocket
224: .isClosed());
225: }
226:
227: /**
228: * Reads packet bytes from transport connection.
229: *
230: * @return packet as byte array or null or empty packet if connection was closed
231: */
232: public byte[] readPacket() throws IOException {
233:
234: // read packet header
235: byte[] header = new byte[Packet.HEADER_SIZE];
236: int off = 0;
237:
238: while (off < Packet.HEADER_SIZE) {
239: try {
240: int bytesRead = input.read(header, off,
241: Packet.HEADER_SIZE - off);
242: if (bytesRead < 0) {
243: break;
244: }
245: off += bytesRead;
246: } catch (IOException e) {
247: // workaround for "Socket Closed" exception if connection was closed
248: break;
249: }
250: }
251:
252: if (off == 0) {
253: return null;
254: }
255: if (off < Packet.HEADER_SIZE) {
256: throw new IOException(
257: "Connection closed in reading packet header");
258: }
259:
260: // extract packet length
261: int len = Packet.getPacketLength(header);
262: if (len < Packet.HEADER_SIZE) {
263: throw new IOException("Wrong packet size detected: " + len);
264: }
265:
266: // allocate packet bytes and store header there
267: byte[] bytes = new byte[len];
268: System.arraycopy(header, 0, bytes, 0, Packet.HEADER_SIZE);
269:
270: // read packet data
271: while (off < len) {
272: int bytesRead = input.read(bytes, off, len - off);
273: if (bytesRead < 0) {
274: break;
275: }
276: off += bytesRead;
277: }
278: if (off < len) {
279: throw new IOException(
280: "Connection closed in reading packet data");
281: }
282:
283: return bytes;
284: }
285:
286: /**
287: * Writes packet bytes to transport connection.
288: *
289: * @param packet
290: * packet as byte array
291: */
292: public void writePacket(byte[] packet) throws IOException {
293: output.write(packet);
294: output.flush();
295: }
296:
297: /**
298: * Performs handshaking for given timeout.
299: *
300: * @param handshakeTimeout timeout for handshaking in milliseconds
301: */
302: protected void handshake(long handshakeTimeout) throws IOException {
303: transportSocket.setSoTimeout((int) handshakeTimeout);
304:
305: try {
306: output.write(HANDSHAKE_STRING.getBytes());
307: output.flush();
308: int len = HANDSHAKE_STRING.length();
309: byte[] bytes = new byte[len];
310: int off = 0;
311: while (off < len) {
312: int bytesRead = input.read(bytes, off, len - off);
313: if (bytesRead < 0) {
314: break;
315: }
316: off += bytesRead;
317: }
318: String response = new String(bytes, 0, off);
319: if (!response.equals(HANDSHAKE_STRING)) {
320: throw new IOException("Unexpected handshake response: "
321: + response);
322: }
323: } finally {
324: transportSocket.setSoTimeout(0);
325: }
326: }
327:
328: /**
329: * Creates input/output streams for connection socket.
330: */
331: protected void createStreams() throws IOException {
332: input = transportSocket.getInputStream();
333: output = transportSocket.getOutputStream();
334: }
335: }
|