it.sauronsoftware.junique.Server Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of de.huxhorn.lilith.3rdparty.junique Show documentation
Show all versions of de.huxhorn.lilith.3rdparty.junique Show documentation
Custom build of junique for Maven Central deployment.
The newest version!
/*
* 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 .
*/
package it.sauronsoftware.junique;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
/**
* A LockServer is started every time a lock has been taken by a caller. A
* LockServer listens on a local port for messages sent on the lock channel.
* Once a message is received the server delivers it to the
* {@link MessageHandler} supplied by the caller.
*
* @author Carlo Pelliccia
*/
class Server implements Runnable, ConnectionListener {
/**
* The lock id.
*/
private String id;
/**
* The message handler used by this server. It could be null if who has
* taken the lock is not interested in message handling.
*/
private MessageHandler messageHandler;
/**
* A synchronization lock, used internally.
*/
private Object synchLock = new Object();
/**
* The underlying server socket listened by this server.
*/
private ServerSocket serverSocket = null;
/**
* The thread used to run the connections handling routine.
*/
private Thread thread = null;
/**
* Established connections.
*/
private ArrayList connections = new ArrayList();
/**
* It builds a ready-to-start lock server.
*
* @param id
* The lock id.
* @param messageHandler
* The message handler used by this server. It could be null if
* who has taken the lock is not interested in message handling.
*/
public Server(String id, MessageHandler messageHandler) {
this.id = id;
this.messageHandler = messageHandler;
}
/**
* It starts the server.
*
* @throws IllegalStateException
* If this server is already started.
*/
public void start() throws IllegalStateException {
synchronized (synchLock) {
// Tests the server status by checking the serverSocket property
// value.
if (serverSocket != null) {
throw new IllegalStateException("JUnique/Server for id \"" + id
+ "\" already started");
}
// Opens the server socket.
try {
serverSocket = new ServerSocket();
SocketAddress socketAddress = new InetSocketAddress("localhost", 0);
serverSocket.bind(socketAddress);
} catch (IOException e) {
// Release the server socket.
if (serverSocket != null) {
try {
serverSocket.close();
} catch (Throwable t) {
;
}
}
// Throws a runtime exception.
throw new RuntimeException(
"Unexpected IOException while starting "
+ "JUnique/Server for id \"" + id + "\"", e);
}
// Starts the connections handling routine.
thread = new Thread(this, "JUnique/Server/" + id);
thread.setDaemon(true);
thread.start();
// Waits for start signal.
do {
try {
synchLock.wait();
break;
} catch (InterruptedException e) {
;
}
} while (true);
}
}
/**
* It stops the server.
*/
public void stop() {
synchronized (synchLock) {
// Tests the server status by checking the serverSocket property
// value.
if (serverSocket == null) {
throw new IllegalStateException("JUnique/Server for id \"" + id
+ "\" not started");
}
// Stops any ongoing communication.
while (connections.size() > 0) {
Connection c = (Connection) connections.remove(0);
c.stop();
}
// Issues an interrupt signal to the secondary thread.
thread.interrupt();
// Close the serverSocket.
try {
serverSocket.close();
} catch (IOException e) {
;
}
// Waiting for server exiting.
do {
try {
thread.join();
break;
} catch (InterruptedException e) {
;
}
} while (true);
// Discards references.
serverSocket = null;
}
}
/**
* Connections handling routine, running on a separate thread.
*/
public void run() {
// Sends start signal.
synchronized (synchLock) {
synchLock.notify();
}
// Multiple connections handled with multi-threading.
while (!Thread.interrupted()) {
try {
Socket incoming = serverSocket.accept();
Connection c = new Connection(id, incoming, this);
synchronized (synchLock) {
connections.add(c);
c.start();
}
} catch (Throwable t) {
;
}
}
}
/**
* It returns the port on which by this server is listening.
*
* @return The port on which by this server is listening.
* @throws IllegalStateException
* If the server is not started.
*/
public int getListenedPort() throws IllegalStateException {
synchronized (synchLock) {
// Tests the server status by checking the serverSocket property
// value.
if (serverSocket == null) {
throw new IllegalStateException("JUnique/Server for id \"" + id
+ "\" not started");
}
// Returns the value.
return serverSocket.getLocalPort();
}
}
/**
* Called to notify a connection close.
*/
public void connectionClosed(Connection connection) {
synchronized (connections) {
connections.remove(connection);
}
}
/**
* Handle a message reception.
*/
public String messageReceived(Connection connection, String message) {
if (messageHandler != null) {
synchronized (messageHandler) { // Thread-safe
return messageHandler.handle(message);
}
}
return null;
}
}