package chatty.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.logging.Logger; /** * Tries to open a server socket on the given port to check if another instance * already did that, in which case it can send a message to the other instance. * Otherwise it will keep the server socket open and listen for messages from * other instances itself. * * @author tduva */ public class SingleInstance { private static final Logger LOGGER = Logger.getLogger(SingleInstance.class.getName()); private static NewInstanceListener listener; /** * Tries to register the current instance to the given port. The port is * used to listen for new instances in case registering succeeds (this is * the first instance and the port isn't yet taken by another program). * Otherwise no action is performed by this function, but you may use * {@link notifyRunningInstance(int, String)} to send info to the already * running instance. * * @param port The port to register * @return true if registering succeeded, false when an error occured (most * likely the port already taken and another instance already running) */ public static boolean registerInstance(int port) { try { final ServerSocket server = new ServerSocket(port, 0, InetAddress.getLoopbackAddress()); Runnable connectionListener = new Runnable() { @Override public void run() { while (!server.isClosed()) { try { Socket socket = server.accept(); handleConnection(socket); } catch (IOException ex) { LOGGER.warning("Error: "+ex); break; } } } }; new Thread(connectionListener).start(); LOGGER.info("Registered port "+port); } catch (IOException ex) { return false; } return true; } /** * Handles an incoming connection from listening to the registered port. * Reads all received text and notifies the listener if there is one. * * @param socket The Socket to the client to receive data on */ private static void handleConnection(Socket socket) { try (BufferedReader reader = new BufferedReader( new InputStreamReader( socket.getInputStream(), "UTF-8") )) { int character; StringBuilder b = new StringBuilder(); while ((character = reader.read()) != -1) { b.append((char)character); } LOGGER.info(String.format("Received instance message: %s [%s]", b.toString(), socket)); if (listener != null) { listener.newInstance(b.toString()); } } catch (IOException ex) { LOGGER.warning("Error handling connection: "+ex); } } /** * Sends the given message to the given port, where the already running * instance should be listening on. * * @param port The port to send the message to * @param message The message to send */ public static void notifyRunningInstance(int port, String message) { try { LOGGER.info("Notifying already running instance: "+message); InetSocketAddress address = new InetSocketAddress( InetAddress.getLoopbackAddress(), port); Socket connection = new Socket(); connection.connect(address, 500); try (PrintWriter output = new PrintWriter( new OutputStreamWriter( connection.getOutputStream(), "UTF-8"))) { output.print(message); } } catch (IOException ex) { LOGGER.warning("Error notifying instance: "+ex); } } /** * Sets the listener to send messages about new instances to. * * @param listener */ public static void setNewInstanceListener(NewInstanceListener listener) { SingleInstance.listener = listener; } public interface NewInstanceListener { /** * Notifies about a message being received from a new instance. * * @param message */ public void newInstance(String message); } public static final void main(String[] args) { // For testing int port = 12345; registerInstance(port); notifyRunningInstance(port, "{\"channel\":\"test\"}"); NewInstanceListener listener = new NewInstanceListener() { @Override public void newInstance(String message) { System.out.println(message); } }; setNewInstanceListener(listener); } }