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();
}
}
}
}