com.notuvy.singleapp.SingleAppEnforcer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of notuvysingleapp Show documentation
Show all versions of notuvysingleapp Show documentation
A simple Java framework for making applications singletons. The underlying mechanism uses interprocess communication to that at most one application process is active at a time. A new application invocation either displaces a previous invocation, or it defers to the previous and terminates itself.
/*
Copyright 2007-2009 Murali Krishnan
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.notuvy.singleapp;
import java.io.*;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import org.apache.log4j.Logger;
/**
* A utility that enforces the invocation of a single application instance.
*
* @author murali
*/
public abstract class SingleAppEnforcer implements Runnable {
//------------------------------------------------------------
//- Class Variables
protected static final Logger LOG = Logger.getLogger(SingleAppEnforcer.class);
//------------------------------------------------------------
//- Class Functions
public static void defer(int pPort, SingleApp pSingleApp) {
SingleAppEnforcer enforcer = new Defer(pPort, pSingleApp);
enforcer.attempt();
}
public static void preempt(int pPort, SingleApp pSingleApp) {
SingleAppEnforcer enforcer = new Preempt(pPort, pSingleApp);
enforcer.attempt();
}
//------------------------------------------------------------
//- Instance Variables
private final int vPort;
private ServerSocket vServer = null;
private final SingleApp vSingleApp;
//------------------------------------------------------------
//- Constructors
protected SingleAppEnforcer(int pPort, SingleApp pSingleApp) {
vPort = pPort;
vSingleApp = pSingleApp;
}
//------------------------------------------------------------
//- Accessors
public int getPort() {
return vPort;
}
private ServerSocket getServer() {
return vServer;
}
private SingleApp getSingleApp() {
return vSingleApp;
}
//------------------------------------------------------------
//- Settors
private void setServer(ServerSocket pServer) {
vServer = pServer;
}
//------------------------------------------------------------
//- Private/Protected Utility Functions
protected void deferControl() {
LOG.error("Not starting; another instance is already running.");
try {
Socket socket = new Socket("localhost", getPort());
Serializable message = getSingleApp().sendFromDeferred();
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
if (message == null) {
oos.writeBoolean(false);
} else {
oos.writeBoolean(true);
oos.writeObject(message);
}
oos.close();
socket.close();
} catch (IOException ioe) {
LOG.error("Problem forwarding on [" + getPort() + "].", ioe);
}
}
protected void wrest() {
try {
Socket socket = new Socket("localhost", getPort());
// This should tell the other application to shut down.
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
boolean hasMessage = ois.readBoolean();
Object message = hasMessage ? ois.readObject() : null;
ois.close();
socket.close();
getSingleApp().recvFromPreempted(message);
try {
Thread.sleep(1000);
// Wait for port to die.
} catch (InterruptedException ie) {
// nothing.
}
setServer(new ServerSocket(getPort(), 1));
} catch (IOException ioe) {
LOG.error("Problem wresting on [" + getPort() + "].", ioe);
} catch (ClassNotFoundException cnfe) {
LOG.error("Undefined class at deserialization.", cnfe);
}
}
protected void takeControl() {
Thread thread = new Thread(this, "SingleAppEnforcer");
thread.setDaemon(true);
thread.start();
getSingleApp().run();
}
//------------------------------------------------------------
//- Public Interface Functions
public void attempt() {
// The first entity to call this will create the socket listener, and thus call "takeControl()".
// All subsequent calls will encounter a port conflict (until the first application quits), and thus call "handlePortConflict()".
try {
setServer(new ServerSocket(getPort(), 1));
takeControl();
} catch (BindException be) {
handlePortConflict();
} catch (IOException ioe) {
LOG.error("Could not listen on [" + getPort() + "].", ioe);
}
}
//------------------------------------------------------------
//- Class Interface Functions
// Note that this will be executed in its own thread. See "takeControl()".
public void run() {
while (true) {
try {
Socket socket = getServer().accept();
handleConnection(getSingleApp(), socket);
socket.close();
} catch (IOException ioe) {
LOG.error("Problem with a new connection.", ioe);
}
}
}
protected abstract void handlePortConflict();
// When the first instance receives a socket connection, handle it.
protected abstract void handleConnection(SingleApp pSingleApp, Socket pSocket);
//------------------------------------------------------------
//- Inner Classes
static class Defer extends SingleAppEnforcer {
protected Defer(int pPort, SingleApp pSingleApp) {
super (pPort, pSingleApp);
}
protected void handlePortConflict() {
deferControl();
}
protected void handleConnection(SingleApp pSingleApp, Socket pSocket) {
LOG.info("Rejected another instance invocation attempt.");
try {
ObjectInputStream ois = new ObjectInputStream(pSocket.getInputStream());
boolean hasMessage = ois.readBoolean();
Object message = hasMessage ? ois.readObject() : null;
ois.close();
pSingleApp.recvFromDeferred(message);
} catch (IOException ioe) {
LOG.error("Could read forwarded data.", ioe);
} catch (ClassNotFoundException cnfe) {
LOG.error("Unexpected class.", cnfe);
}
}
}
static class Preempt extends SingleAppEnforcer {
protected Preempt(int pPort, SingleApp pSingleApp) {
super (pPort, pSingleApp);
}
protected void handlePortConflict() {
wrest();
takeControl();
}
protected void handleConnection(SingleApp pSingleApp, Socket pSocket) {
try {
super.getServer().close(); // Stop listening for new connections (there will be none).
Serializable message = pSingleApp.sendToPreemptor();
ObjectOutputStream oos = new ObjectOutputStream(pSocket.getOutputStream());
if (message == null) {
oos.writeBoolean(false);
} else {
oos.writeBoolean(true);
oos.writeObject(message);
}
oos.close();
pSocket.close();
} catch (IOException ioe) {
LOG.error("Problem sending to preemptor on [" + getPort() + "].", ioe);
}
pSingleApp.gracefulExitForPreempt();
System.exit(0); // Force death if not graceful.
}
}
//------------------------------------------------------------
//- Main
}