xref: /AOO41X/test/testcommon/source/org/openoffice/test/vcl/client/CommunicationManager.java (revision e6e6073ddaad3a04a985e8f05823629a884eb203)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package org.openoffice.test.vcl.client;
25 
26 import java.io.DataInputStream;
27 import java.io.DataOutputStream;
28 import java.io.IOException;
29 import java.net.InetSocketAddress;
30 import java.net.Socket;
31 import java.util.List;
32 import java.util.Vector;
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35 
36 import org.openoffice.test.common.SystemUtil;
37 
38 /**
39  * Manage the communication with the automation server.
40  * It's used to establish the connection, send and receive data package.
41  * Data package format:
42  *
43  * | [Force Multi Channel (0xFFFFFFFF)] | Data Length (32 bits) | Check Byte (8 bits) | Header Length (16 bits) | Header Data | Body Data |
44  *
45  * To handle the received data, add a communication listener to the manager.
46  * The listner will be called back when data package arrives.
47  *
48  */
49 public class CommunicationManager implements Runnable, Constant{
50 
51     private static Logger logger = Logger.getLogger("CommunicationManager");
52 
53     private final static int DEFAULT_PORT = 12479;
54 
55     private String host = "localhost";
56 
57     private int port = DEFAULT_PORT;
58 
59     private Socket socket = null;
60 
61     private double reconnectInterval = 2;
62 
63     private int reconnectCount = 5;
64 
65     private List<CommunicationListener> listeners = new Vector<CommunicationListener>();
66 
67     /**
68      * Create a communication manager with the default host and port.
69      * The default host is local and the default port is 12479.
70      *
71      */
CommunicationManager()72     public CommunicationManager() {
73         try {
74             String portValue = System.getProperty("openoffice.automation.port");
75             if (portValue != null)
76                 port = Integer.parseInt(portValue);
77         } catch (NumberFormatException e) {
78             // use default
79         }
80     }
81 
82     /**
83      * Create a communication manager with the given host and port
84      * @param host
85      * @param port
86      */
CommunicationManager(String host, int port)87     public CommunicationManager(String host, int port) {
88         this.host = host;
89         this.port = port;
90     }
91 
92     /**
93      * Send a data package to server
94      * @param headerType the package header type
95      * @param header the data in the header
96      * @param data the data in the body
97      */
sendPackage(int headerType, byte[] header, byte[] data)98     public synchronized void sendPackage(int headerType, byte[] header, byte[] data) throws CommunicationException {
99         if (socket == null)
100             start();
101 
102         try {
103             if (header == null)
104                 header = new byte[0];
105 
106             if (data == null)
107                 data = new byte[0];
108 
109             DataOutputStream os = new DataOutputStream(socket.getOutputStream());
110             int len = 1 + 2 + 2 + header.length + data.length;
111             // Total len
112             os.writeInt(len);
113             // Check byte
114             os.writeByte(calcCheckByte(len));
115             // Header len
116             os.writeChar(2 + header.length);
117             // Header
118             os.writeChar(headerType);
119             os.write(header);
120             // Data
121             os.write(data);
122             os.flush();
123         } catch (IOException e) {
124             stop();
125             throw new CommunicationException("Failed to send data to automation server!", e);
126         }
127     }
128 
129     /**
130      * Start a new thread to read the data sent by sever
131      */
run()132     public void run() {
133         try {
134             while (socket != null) {
135                 DataInputStream is = new DataInputStream(socket.getInputStream());
136 
137                 int len = is.readInt();
138                 if (len == 0xFFFFFFFF)
139                     len = is.readInt();
140 
141                 byte checkByte = is.readByte();
142                 if (calcCheckByte(len) != checkByte)
143                     throw new CommunicationException("Bad data package. Wrong check byte.");
144 
145                 int headerLen = is.readUnsignedShort();
146                 int headerType = is.readUnsignedShort();
147                 byte[] header = new byte[headerLen - 2];
148                 is.readFully(header);
149                 byte[] data = new byte[len - headerLen - 3];
150                 is.readFully(data);
151                 for (int i = 0; i < listeners.size(); i++)
152                     ((CommunicationListener) listeners.get(i)).received(headerType, header, data);
153             }
154         } catch (Exception e) {
155             logger.log(Level.FINEST, "Failed to receive data!", e);
156             stop();
157         }
158     }
159 
160     /**
161      * Add a communication listener
162      * @param listener
163      */
addListener(CommunicationListener listener)164     public void addListener(CommunicationListener listener) {
165         if (listener != null && !listeners.contains(listener))
166             listeners.add(listener);
167     }
168 
169 
170     /**
171      * Stop the communication manager.
172      *
173      */
stop()174     public synchronized void stop() {
175         if (socket == null)
176             return;
177 
178         try {
179             socket.close();
180         } catch (IOException e) {
181             //ignore
182         }
183         socket = null;
184         logger.log(Level.CONFIG, "Stop Communication Manager");
185         for (int i = 0; i < listeners.size(); i++)
186             ((CommunicationListener) listeners.get(i)).stop();
187     }
188 
isConnected()189     public synchronized boolean isConnected() {
190         return socket != null;
191     }
192 
193 
connect()194     public synchronized void connect() throws IOException {
195         if (socket != null)
196             return;
197 
198         try{
199             socket = new Socket();
200             socket.setTcpNoDelay(true);
201             socket.setSoTimeout(240 * 1000); // if in 4 minutes we get nothing from server, an exception will thrown.
202             socket.connect(new InetSocketAddress(host, port));
203             Thread thread = new Thread(this);
204             thread.setDaemon(true);
205             thread.start();
206         } catch (IOException e){
207             socket = null;
208             throw e;
209         }
210     }
211 
212     /**
213      * Start the communication manager.
214      *
215      */
start()216     public synchronized void start() {
217         logger.log(Level.CONFIG, "Start Communication Manager");
218         //connect and retry if fails
219         for (int i = 0; i < reconnectCount; i++) {
220             try {
221                 connect();
222                 return;
223             } catch (IOException e) {
224                 logger.log(Level.FINEST, "Failed to connect! Tried " + i, e);
225             }
226 
227             SystemUtil.sleep(reconnectInterval);
228         }
229 
230         throw new CommunicationException("Failed to connect to automation server on: " + host + ":" + port);
231     }
232 
233 
calcCheckByte(int i)234     private static byte calcCheckByte(int i) {
235         int nRes = 0;
236         int[] bytes = new int[4];
237         bytes[0] = (i >>> 24) & 0x00FF;
238         bytes[1] = (i >>> 16) & 0x00FF;
239         bytes[2] = (i >>> 8) & 0x00FF;
240         bytes[3] = (i >>> 0) & 0x00FF;
241         nRes += bytes[0] ^ 0xf0;
242         nRes += bytes[1] ^ 0x0f;
243         nRes += bytes[2] ^ 0xf0;
244         nRes += bytes[3] ^ 0x0f;
245         nRes ^= (nRes >>> 8);
246         return (byte) nRes;
247     }
248 }
249