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