All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.sauronsoftware.junique.Server Maven / Gradle / Ivy

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

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy