/*
* JUnique - Helps in preventing multiple instances of the same application
*
* Copyright (C) 2008-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package it.sauronsoftware.junique;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* This class encapsulates any connection received by the server.
*
* @author Carlo Pelliccia
*/
class Connection implements Runnable {
/**
* A plain internal synchronization object.
*/
private Object synchLock = new Object();
/**
* The lock ID.
*/
private String id;
/**
* The connection listener.
*/
private ConnectionListener listener;
/**
* The underlying socket connection.
*/
private Socket socket;
/**
* The stream for reading what client sent.
*/
private InputStream inputStream;
/**
* The stream for sending bytes to the client.
*/
private OutputStream outputStream;
/**
* A running flag. When true the connection handling routine is started.
*/
private boolean running = false;
/**
* Secondary thread executing the reading routine.
*/
private Thread thread;
/**
* It builds the connection.
*
* @param id
* The associated lock id.
* @param socket
* The underlying socket connection.
* @param listener
* A connection listener (required).
*/
public Connection(String id, Socket socket, ConnectionListener listener) {
this.id = id;
this.socket = socket;
this.listener = listener;
}
/**
* It starts the connection handling routine.
*
* @throws IllegalStateException
* If the connection routine has already been started.
*/
public void start() throws IllegalStateException {
synchronized (synchLock) {
// Status check.
if (running) {
throw new IllegalStateException("JUnique/Server/" + id
+ "/Connection already started");
}
// Running flag update.
running = true;
// Starts the secondary thread.
thread = new Thread(this, "JUnique/Server/" + id + "/Connection");
thread.setDaemon(true);
thread.start();
// Waits for start signal.
do {
try {
synchLock.wait();
break;
} catch (InterruptedException e) {
;
}
} while (true);
}
}
/**
* It stops the connection handling routine.
*
* @throws IllegalStateException
* If the connection routine is not started.
*/
public void stop() throws IllegalStateException {
synchronized (synchLock) {
// Status check.
if (!running) {
throw new IllegalStateException("JUnique/Server/" + id
+ "/Connection not started");
}
// Running flag update.
running = false;
// Issues an interrupt signal to the secondary thread.
thread.interrupt();
// Closes any underlying stream.
try {
inputStream.close();
} catch (IOException e) {
;
}
try {
outputStream.close();
} catch (IOException e) {
;
}
try {
socket.close();
} catch (IOException e) {
;
}
// Waiting for server exiting.
if (Thread.currentThread() != thread) {
do {
try {
thread.join();
break;
} catch (InterruptedException e) {
;
}
} while (true);
}
// Discards references.
socket = null;
inputStream = null;
outputStream = null;
}
}
/**
* The connection handling routine.
*/
public void run() {
// Sends start signal.
synchronized (synchLock) {
synchLock.notify();
}
// Streams retrieval.
try {
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
} catch (IOException e) {
stop();
}
// Loop.
while (!Thread.interrupted()) {
try {
String message = Message.read(inputStream);
String response = listener.messageReceived(this, message);
if (response == null) {
response = "";
}
Message.write(response, outputStream);
} catch (IOException e) {
stop();
}
}
// Listener notification.
listener.connectionClosed(this);
}
}