package net.sourceforge.pain.tools.guitool; import net.sourceforge.pain.network.guitool.*; import net.sourceforge.pain.tools.guitool.dialog.*; import java.io.*; import java.net.*; import java.util.*; public class GTClientConnection { private int sequence_id = 0; private long lastEventTime; private String host; private int port; private String login; private String password; private Socket socket; private ObjectInputStream is; private ObjectOutputStream os; private ConnectionPinger checker; private Receiver receiver; private Sender sender; public GTClientConnection(String host, int port, String login, String password) throws Exception { this.host = host; this.port = port; this.login = login; this.password = password; connect(); checker = new ConnectionPinger(); checker.start(); } private void connect() throws Exception { socket = new Socket(); socket.setSoTimeout(10000); socket.connect(new InetSocketAddress(host, port)); boolean ok = false; try { os = new ObjectOutputStream(socket.getOutputStream()); is = new ObjectInputStream(socket.getInputStream()); receiver = new Receiver(); sender = new Sender(); login(); receiver.start(); sender.start(); ok = true; } finally { if (!ok) { disconnect(); } } } private void login() throws Exception { GTNetPacket request = new GTNetPacket("Login", new String[]{login, password}, nextSequenceId()); sendPacket(request); GTNetPacket reply = (GTNetPacket) is.readObject(); if (!"INFO".equals(reply.eventClassName)) { throw new RuntimeException("Login error:" + reply.data); } } private synchronized void sendPacket(GTNetPacket packet) throws IOException { os.writeObject(packet); os.flush(); lastEventTime = System.currentTimeMillis(); } public void close() { if (socket != null) { disconnect(); } } private void onRemoteDisconnect() { disconnect(); GlobalPerformer.onRemoteDisconnect(); } private void disconnect() { try { Socket tmp = socket; socket = null; is = null; os = null; tmp.close(); } catch (Exception e) { e.printStackTrace(); } if (checker != null) { checker.die(); checker = null; } if (receiver != null) { receiver.die(); receiver = null; } if (sender != null) { sender.die(); sender = null; } } public void addGTConnectionEventListener(GTConnectionListener listener) { receiver.addListener(listener); } public void removeGTConnectionEventListener(GTConnectionListener listener) { receiver.removeListener(listener); } public synchronized int nextSequenceId() { return ++sequence_id; } /** temporary method impl*/ public GTNetPacket sendBlocking(final GTNetPacket p) throws IOException, InterruptedException { final ArrayList result = new ArrayList(); GTConnectionListener l = new GTConnectionListener() { public Object onPacketReceived(GTNetPacket packet) { if (packet.sequence_id == p.sequence_id) { result.add(packet); removeGTConnectionEventListener(this); synchronized (result) { result.notify(); } } return null; } }; addGTConnectionEventListener(l); synchronized (result) { sendPacket(p); result.wait(); } return (GTNetPacket) result.get(0); } private final class ConnectionPinger extends Thread { private boolean isAlive = true; public ConnectionPinger() { setDaemon(true); } public void run() { while (isAlive) { try { synchronized (this) { wait(5000L); } if (lastEventTime + 5000L > System.currentTimeMillis()) { continue; } sendPacket(new GTNetPacket("Ping", "", nextSequenceId())); } catch (InterruptedException e) { break; } catch (Exception e) { if (socket != null) { e.printStackTrace(); onRemoteDisconnect(); } break; } } } public void die() { isAlive = false; synchronized (this) { notify(); } } } private final class Receiver extends Thread { private boolean isAlive = true; final HashSet listeners = new HashSet(); final HashSet toRemove = new HashSet(); final HashSet toAdd = new HashSet(); public Receiver() { setDaemon(true); } public void run() { while (isAlive) { try { checkAddRemove(); GTNetPacket p = (GTNetPacket) is.readObject(); checkAddRemove(); for (Iterator it = listeners.iterator(); it.hasNext();) { GTConnectionListener l = (GTConnectionListener) it.next(); try { l.onPacketReceived(p); } catch (Exception e) { e.printStackTrace(); } } } catch (Exception e) { if (socket != null) { e.printStackTrace(); onRemoteDisconnect(); } break; } } } private void checkAddRemove() { synchronized (listeners) { if (!toAdd.isEmpty()) { listeners.addAll(toAdd); toAdd.clear(); } if (!toRemove.isEmpty()) { listeners.removeAll(toRemove); toRemove.clear(); } } } public void addListener(GTConnectionListener l) { synchronized (listeners) { toAdd.add(l); } } public void removeListener(GTConnectionListener l) { synchronized (listeners) { toRemove.add(l); } } public void die() { isAlive = false; synchronized (this) { notify(); } } } private final class Sender extends Thread { private boolean isAlive = true; private LinkedList outQueue = new LinkedList(); public Sender() { setDaemon(true); } public void run() { while (isAlive) { try { if (outQueue.isEmpty()) { synchronized (outQueue) { outQueue.wait(50); } continue; } synchronized (outQueue) { while (!outQueue.isEmpty()) { GTNetPacket p = (GTNetPacket) outQueue.removeFirst(); os.writeObject(p); } } os.flush(); } catch (Exception e) { if (socket != null) { e.printStackTrace(); onRemoteDisconnect(); } break; } } } public void send(GTNetPacket p) { synchronized (outQueue) { outQueue.add(p); outQueue.notify(); } } public void die() { isAlive = false; synchronized (this) { notify(); } } } }