1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18 
19 /**
20  * @author Vitaly A. Provodin
21  */
22 
23 /**
24  * Created on 29.01.2005
25  */
26 package org.apache.harmony.jpda.tests.share;
27 
28 import java.io.DataInputStream;
29 import java.io.DataOutputStream;
30 import java.io.EOFException;
31 import java.io.IOException;
32 import java.net.InetAddress;
33 import java.net.InetSocketAddress;
34 import java.net.Socket;
35 import java.net.ServerSocket;
36 import java.net.UnknownHostException;
37 
38 import org.apache.harmony.jpda.tests.framework.DebuggeeSynchronizer;
39 import org.apache.harmony.jpda.tests.framework.LogWriter;
40 import org.apache.harmony.jpda.tests.framework.TestErrorException;
41 import org.apache.harmony.jpda.tests.framework.TestOptions;
42 
43 /**
44  * This class implements <code>DebuggeeSynchronizer</code> interface using
45  * TCP/IP sockets. All operations can be timed out according to default timeout.
46  */
47 public class JPDADebuggeeSynchronizer implements DebuggeeSynchronizer {
48 
49     public final static String SGNL_READY = "ready";
50 
51     public final static String SGNL_CONTINUE = "continue";
52 
53     protected Socket clientSocket = null;
54 
55     protected ServerSocket serverSocket = null;
56 
57     protected DataOutputStream out;
58 
59     protected DataInputStream in;
60 
61     protected TestOptions settings;
62 
63     protected LogWriter logWriter;
64 
65     /**
66      * A constructor that initializes an instance of the class with specified
67      * <code>LogWriter</code> and <code>TestOptions</code>.
68      *
69      * @param logWriter
70      *            The instance of implementation of LogWriter.
71      * @param settings
72      *            Instance of test options.
73      */
JPDADebuggeeSynchronizer(LogWriter logWriter, TestOptions settings)74     public JPDADebuggeeSynchronizer(LogWriter logWriter, TestOptions settings) {
75         super();
76         this.logWriter = logWriter;
77         this.settings = settings;
78     }
79 
80     /**
81      * Sends specified message to synchronization channel.
82      *
83      * @param message
84      *            a message to be sent.
85      */
sendMessage(String message)86     public synchronized void sendMessage(String message) {
87         try {
88             out.writeUTF(message);
89             out.flush();
90             logWriter.println("[SYNC] Message sent: " + message);
91         } catch (IOException e) {
92             throw new TestErrorException(e);
93         }
94     }
95 
96     /**
97      * Receives message from synchronization channel and compares it with the
98      * expected <code>message</code>.
99      *
100      * @param message
101      *            expected message.
102      * @return <code>true</code> if received string is equals to
103      *         <code>message</code> otherwise - <code>false</code>.
104      *
105      */
receiveMessage(String message)106     public synchronized boolean receiveMessage(String message) {
107         String msg;
108         try {
109             logWriter.println("[SYNC] Waiting for message: " + message);
110             msg = in.readUTF();
111             logWriter.println("[SYNC] Received message: " + msg);
112         } catch (EOFException e) {
113             return false;
114         } catch (IOException e) {
115             logWriter.printError(e);
116             return false;
117         }
118         return message.equalsIgnoreCase(msg);
119     }
120 
121     /**
122      * Receives message from synchronization channel.
123      *
124      * @return received string or null if connection was closed.
125      */
receiveMessage()126     public synchronized String receiveMessage() {
127         String msg;
128         try {
129             logWriter.println("[SYNC] Waiting for any messsage");
130             msg = in.readUTF();
131             logWriter.println("[SYNC] Received message: " + msg);
132         } catch (EOFException e) {
133             return null;
134         } catch (IOException e) {
135             throw new TestErrorException(e);
136         }
137         return msg;
138     }
139 
140     /**
141      * Receives message from synchronization channel without Exception.
142      *
143      * @return received string
144      */
receiveMessageWithoutException(String invoker)145     public synchronized String receiveMessageWithoutException(String invoker) {
146         String msg;
147         try {
148             logWriter.println("[SYNC] Waiting for any message");
149             msg = in.readUTF();
150             logWriter.println("[SYNC] Received message: " + msg);
151         } catch (Throwable thrown) {
152             if (invoker != null) {
153                 logWriter.println("#### receiveMessageWithoutException: Exception occurred:");
154                 logWriter.println("#### " + thrown);
155                 logWriter.println("#### Invoker = " + invoker);
156             }
157             msg = "" + thrown;
158         }
159         return msg;
160     }
161 
162     /**
163      * Returns socket address for connecting to the server.
164      *
165      * If <code>serverAddress.getPort()</code> returns 0 (i.e.,
166      * <code>org.apache.harmony.jpda.tests.framework.TestOptions.DEFAULT_SYNC_PORT</code>),
167      * a port will automatically be chosen by the OS when the server is bound to a socket.
168      *
169      * @return socket address
170      */
getSyncServerAddress()171     public InetSocketAddress getSyncServerAddress() {
172         // Use the LOOPBACK directly instead of doing a DNS lookup.
173         int port = settings.getSyncPortNumber();
174         try {
175             // Use IPv4 to ensure we do not depend on IPv6 to run these tests.
176             // TODO(25178637): Use InetAddress.getLoopbackAddress() instead.
177             return new InetSocketAddress(
178                 InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }), port);
179         } catch (UnknownHostException e) {
180             throw new TestErrorException(
181                     "[SYNC] Exception in binding for socket sync connection", e);
182         }
183     }
184 
185     /**
186      * Binds server to listen socket port.
187      *
188      * If <code>serverAddress.getPort()</code> returns 0 (i.e.,
189      * <code>org.apache.harmony.jpda.tests.framework.TestOptions.DEFAULT_SYNC_PORT</code>),
190      * the OS will choose a port automatically for this server socket.
191      *
192      * @return port number
193      */
bindServer()194     public synchronized int bindServer() {
195         InetSocketAddress serverAddress = getSyncServerAddress();
196         try {
197             logWriter.println("[SYNC] Binding socket on: " + serverAddress);
198             serverSocket = new ServerSocket(serverAddress.getPort(), 0, serverAddress.getAddress());
199             int localPort = serverSocket.getLocalPort();
200             logWriter.println("[SYNC] Bound socket on: " + serverAddress
201                     + " (local port: " + localPort + ")" );
202             return localPort;
203         } catch (IOException e) {
204             throw new TestErrorException(
205                     "[SYNC] Exception in binding for socket sync connection", e);
206         }
207     }
208 
209     /**
210      * Accepts sync connection form server side.
211      */
startServer()212     public synchronized void startServer() {
213         long timeout = settings.getTimeout();
214         try {
215             serverSocket.setSoTimeout((int) timeout);
216             logWriter.println("[SYNC] Accepting socket connection");
217             clientSocket = serverSocket.accept();
218             logWriter.println("[SYNC] Accepted socket connection");
219 
220             clientSocket.setSoTimeout((int) timeout);
221             out = new DataOutputStream(clientSocket.getOutputStream());
222             in = new DataInputStream(clientSocket.getInputStream());
223         } catch (IOException e) {
224             throw new TestErrorException(
225                     "[SYNC] Exception in accepting socket sync connection", e);
226         }
227     }
228 
229     /**
230      * Attaches for sync connection from client side..
231      */
startClient()232     public synchronized void startClient() {
233         long timeout = settings.getTimeout();
234         InetSocketAddress serverAddress = getSyncServerAddress();
235         try {
236             logWriter.println("[SYNC] Attaching socket to: " + serverAddress);
237             clientSocket = new Socket(serverAddress.getAddress(), serverAddress.getPort());
238             logWriter.println("[SYNC] Attached socket");
239 
240             clientSocket.setSoTimeout((int) timeout);
241             out = new DataOutputStream(clientSocket.getOutputStream());
242             in = new DataInputStream(clientSocket.getInputStream());
243         } catch (IOException e) {
244             throw new TestErrorException(
245                     "[SYNC] Exception in attaching for socket sync connection", e);
246         }
247     }
248 
249     /**
250      * Stops synchronization work. It ignores <code>IOException</code> but
251      * prints a message.
252      */
stop()253     public void stop() {
254         try {
255             if (out != null)
256                 out.close();
257             if (in != null)
258                 in.close();
259             if (clientSocket != null)
260                 clientSocket.close();
261             if (serverSocket != null)
262                 serverSocket.close();
263         } catch (IOException e) {
264             logWriter.println
265                     ("[SYNC] Ignoring exception in closing socket sync connection: " + e);
266         }
267         logWriter.println("[SYNC] Closed socket");
268     }
269 }
270