blob: 6310dfe0cd8449d5ed946e270f33f947a99d55cb [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
package org.openoffice.test.vcl.client;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openoffice.test.common.SystemUtil;
/**
* Manage the communication with the automation server.
* It's used to establish the connection, send and receive data package.
* Data package format:
*
* | [Force Multi Channel (0xFFFFFFFF)] | Data Length (32 bits) | Check Byte (8 bits) | Header Length (16 bits) | Header Data | Body Data |
*
* To handle the received data, add a communication listener to the manager.
* The listner will be called back when data package arrives.
*
*/
public class CommunicationManager implements Runnable, Constant{
private static Logger logger = Logger.getLogger("CommunicationManager");
private final static int DEFAULT_PORT = 12479;
private String host = "localhost";
private int port = DEFAULT_PORT;
private Socket socket = null;
private double reconnectInterval = 2;
private int reconnectCount = 5;
private List<CommunicationListener> listeners = new Vector<CommunicationListener>();
/**
* Create a communication manager with the default host and port.
* The default host is local and the default port is 12479.
*
*/
public CommunicationManager() {
try {
String portValue = System.getProperty("openoffice.automation.port");
if (portValue != null)
port = Integer.parseInt(portValue);
} catch (NumberFormatException e) {
// use default
}
}
/**
* Create a communication manager with the given host and port
* @param host
* @param port
*/
public CommunicationManager(String host, int port) {
this.host = host;
this.port = port;
}
/**
* Send a data package to server
* @param headerType the package header type
* @param header the data in the header
* @param data the data in the body
*/
public synchronized void sendPackage(int headerType, byte[] header, byte[] data) throws CommunicationException {
if (socket == null)
start();
try {
if (header == null)
header = new byte[0];
if (data == null)
data = new byte[0];
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
int len = 1 + 2 + 2 + header.length + data.length;
// Total len
os.writeInt(len);
// Check byte
os.writeByte(calcCheckByte(len));
// Header len
os.writeChar(2 + header.length);
// Header
os.writeChar(headerType);
os.write(header);
// Data
os.write(data);
os.flush();
} catch (IOException e) {
stop();
throw new CommunicationException("Failed to send data to automation server!", e);
}
}
/**
* Start a new thread to read the data sent by sever
*/
public void run() {
try {
while (socket != null) {
DataInputStream is = new DataInputStream(socket.getInputStream());
int len = is.readInt();
if (len == 0xFFFFFFFF)
len = is.readInt();
byte checkByte = is.readByte();
if (calcCheckByte(len) != checkByte)
throw new CommunicationException("Bad data package. Wrong check byte.");
int headerLen = is.readUnsignedShort();
int headerType = is.readUnsignedShort();
byte[] header = new byte[headerLen - 2];
is.readFully(header);
byte[] data = new byte[len - headerLen - 3];
is.readFully(data);
for (int i = 0; i < listeners.size(); i++)
((CommunicationListener) listeners.get(i)).received(headerType, header, data);
}
} catch (Exception e) {
logger.log(Level.FINEST, "Failed to receive data!", e);
stop();
}
}
/**
* Add a communication listener
* @param listener
*/
public void addListener(CommunicationListener listener) {
if (listener != null && !listeners.contains(listener))
listeners.add(listener);
}
/**
* Stop the communication manager.
*
*/
public synchronized void stop() {
if (socket == null)
return;
try {
socket.close();
} catch (IOException e) {
//ignore
}
socket = null;
logger.log(Level.CONFIG, "Stop Communication Manager");
for (int i = 0; i < listeners.size(); i++)
((CommunicationListener) listeners.get(i)).stop();
}
public synchronized boolean isConnected() {
return socket != null;
}
public synchronized void connect() throws IOException {
if (socket != null)
return;
try{
socket = new Socket();
socket.setTcpNoDelay(true);
socket.setSoTimeout(240 * 1000); // if in 4 minutes we get nothing from server, an exception will thrown.
socket.connect(new InetSocketAddress(host, port));
Thread thread = new Thread(this);
thread.setDaemon(true);
thread.start();
} catch (IOException e){
socket = null;
throw e;
}
}
/**
* Start the communication manager.
*
*/
public synchronized void start() {
logger.log(Level.CONFIG, "Start Communication Manager");
//connect and retry if fails
for (int i = 0; i < reconnectCount; i++) {
try {
connect();
return;
} catch (IOException e) {
logger.log(Level.FINEST, "Failed to connect! Tried " + i, e);
}
SystemUtil.sleep(reconnectInterval);
}
throw new CommunicationException("Failed to connect to automation server on: " + host + ":" + port);
}
private static byte calcCheckByte(int i) {
int nRes = 0;
int[] bytes = new int[4];
bytes[0] = (i >>> 24) & 0x00FF;
bytes[1] = (i >>> 16) & 0x00FF;
bytes[2] = (i >>> 8) & 0x00FF;
bytes[3] = (i >>> 0) & 0x00FF;
nRes += bytes[0] ^ 0xf0;
nRes += bytes[1] ^ 0x0f;
nRes += bytes[2] ^ 0xf0;
nRes += bytes[3] ^ 0x0f;
nRes ^= (nRes >>> 8);
return (byte) nRes;
}
}