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

com.notuvy.singleapp.SingleAppEnforcer Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 2.1
Show newest version
/*
   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



}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy