/*******************************************************************************
* Copyright (c) 2005 - 2007 committers of openArchitectureWare and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* committers of openArchitectureWare - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.mwe.internal.core.debug.communication;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
import org.eclipse.emf.mwe.internal.core.debug.communication.packages.AbstractPackage;
import org.eclipse.emf.mwe.internal.core.debug.communication.packages.HandshakePackage;
/**
* This class implements a socket connection model. "Packages" (units of information) can be transfered between two
* different virtual machines. During the initialisation of a socket connection a reader and a writer thread are
* established. This class can be used both on the sender and receiver side.
*/
public class Connection {
private ServerSocket ssocket;
private Socket socket;
private DataOutputStream out;
private DataInputStream in;
private PackageSender sender;
private PackageReceiver receiver;
// -------------------------------------------------------------------------
// *the* main operation methods
public AbstractPackage listenForPackage(final Class<? extends AbstractPackage> type) throws InterruptedIOException {
return receiver.getPackage(type, -1);
}
public AbstractPackage listenForPackage(final Class<? extends AbstractPackage> type, final int refId) throws InterruptedIOException {
return receiver.getPackage(type, refId, -1);
}
public int sendPackage(final AbstractPackage packet) throws IOException {
if (sender == null) {
return -1;
}
return sender.sendPackage(packet);
}
// -------------------------------------------------------------------------
// socket communication handling
/**
* create a new <code>ServerSocket</code>.
*
* @param port the communication port
* @throws IOException
*/
public void startListeningSocket(final int port) throws IOException {
ssocket = new ServerSocket(port);
}
/**
* the server listens for a client to connect, creates new reader and writer threads and sends a first
* handshake packet to test the communication
*
* @param timeout
* @throws IOException
*/
public void accept(final int timeout) throws IOException {
ssocket.setSoTimeout(timeout);
// will throw SocketTimeoutException, if nobody connects
socket = ssocket.accept();
establishReaderAndWriter();
if (!writeHandshake()) {
throw new IOException("handshake failed");
}
}
/**
* the client establishes the connection here, creates new reader and writer threads and waits for a handshake
* packet to be received from the server
*
* @param port
* @throws IOException
*/
public void connect(final int port) throws IOException {
socket = new Socket("localhost", port);
establishReaderAndWriter();
replyHandshake();
}
/**
* @return if the socket connection is still active
*/
public boolean isConnected() {
return (socket != null) && !socket.isClosed();
}
/**
* close and dispose the socket and the (possibly waiting) sender thread
*/
public synchronized void close() {
try {
if (sender != null) {
sender.close();
sender = null;
}
if (socket != null) {
socket.close();
socket = null;
}
if(out!=null) {
out.close();
out = null;
}
if(in!=null) {
in.close();
in = null;
}
if(ssocket != null) {
ssocket.close();
ssocket = null;
}
} catch (Exception e) {
// IOExceptions and NullPointerExceptions may occur
}
}
// -------------------------------------------------------------------------
// the real sending and receiving of packages to be called from PackageReceiver and PackageSender only
protected AbstractPackage readPackage() throws IOException {
String className = in.readUTF();
AbstractPackage packet = instantiatePackage(className);
packet.readContent(in);
// System.out.println(Thread.currentThread().getName() + "-RECEIVED-: " + packet);
return packet;
}
@SuppressWarnings("unchecked")
private AbstractPackage instantiatePackage(final String className) throws IOException {
Class<? extends AbstractPackage> packetClass = null;
AbstractPackage packet = null;
String msg = null;
try {
packetClass = (Class<? extends AbstractPackage>) Class.forName(className);
} catch (ClassNotFoundException e) {
msg = "Couldn't find " + className + " in the classpath.";
}
if (msg == null && packetClass != null) {
Constructor<?> c = packetClass.getConstructors()[0];
Class<?>[] parmTypes = c.getParameterTypes();
Object[] initargs = new Object[parmTypes.length];
for (int i = 0; i < parmTypes.length; i++) {
if (parmTypes[i] == int.class) {
initargs[i] = 0;
}
}
try {
packet = (AbstractPackage) c.newInstance(initargs);
} catch (IllegalArgumentException e) {
msg = "Couldn't instantiate " + c + " : " + e;
} catch (InstantiationException e) {
msg = "Couldn't instantiate " + c + " : " + e;
} catch (IllegalAccessException e) {
msg = "Couldn't instantiate " + c + " : " + e;
} catch (InvocationTargetException e) {
msg = "Problem during instantiation of " + c + " : " + e.getTargetException();
}
}
if (msg != null) {
System.err.println(msg);
throw new IOException(msg);
}
return packet;
}
protected void writePackage(final AbstractPackage packet) throws IOException {
out.writeUTF(packet.getClass().getName());
packet.writeContent(out);
// System.out.println(Thread.currentThread().getName() + "-SENT-----: " + packet);
}
// ********************************************************* private methods
private void establishReaderAndWriter() throws IOException {
out = new DataOutputStream(socket.getOutputStream());
in = new DataInputStream(socket.getInputStream());
// start receiver and sender in extra threads
receiver = PackageReceiver.newPackageReceiver(this);
sender = PackageSender.newPackageSender(this);
}
private boolean writeHandshake() throws IOException {
AbstractPackage packet = new HandshakePackage();
sendPackage(packet);
return listenForPackage(HandshakePackage.class).getClass().equals(HandshakePackage.class);
}
private void replyHandshake() throws IOException {
if (listenForPackage(HandshakePackage.class).getClass().equals(HandshakePackage.class)) {
sendPackage(new HandshakePackage());
} else {
throw new IOException("handshake failed");
}
}
}