Preventing multiple instance standalone desktop gui applications

Discussion in 'Java' started by Kent Yang, Sep 23, 2004.

  1. Kent Yang

    Kent Yang Guest

    I need to have only one instance of a Desktop GUI application running.
    What is the best way to do this? The only answer I can find
    searching throught the archives was to use a socket. Is this the best
    way?

    I thought about using rmi (registry) and binding a unique name however
    the problem is if the appliction start the registry, when it exits, it
    takes the registry down with it.

    Is there a cleaner way to do this. I've seen this in several IDE(s)
    Eclipse and JBuilder. How is it done in those apps?

    Thanks in advance,

    Kent
     
    Kent Yang, Sep 23, 2004
    #1
    1. Advertisements

  2. Kent Yang

    Paul Lutus Guest

    That depends. I often use a flag file. This has the advantage that, in a
    multi-user environment, each user has a separate flag file, so each user
    can only have one instance running, but there can be as many single
    instances as there are users.

    I put the flag file in the user's home directory under a subdirectory with
    the same name as the application, a scheme that has been worked out in
    Linux/Unix environments over time, and one that works fine in Windows as
    well.
     
    Paul Lutus, Sep 23, 2004
    #2
    1. Advertisements

  3. I've used a similar technique, though i call it a lock file. In fact, I
    go further and apply a FileLock on the file, which I hold until I'm
    ready to shut down. If the lock attempt fails, it's because the file is
    locked by another instance. If it succeeds, than any pre-existing file
    could've been left behind by a crashed instance of my app (rare though
    that is). It works well for me, with the odd caveat that I can actually
    remove the file manually on Linux while the app is running to bypass
    this restriction.

    = Steve =
     
    Steve W. Jackson, Sep 23, 2004
    #3
  4. If you have JDK1.4+, you can use a flag file with a lock mechanism.
    Here is a little utility class that create/lock a file on user.home folder
    and with name based on the app name provided:

    --------------------------------------------------------------------------------
    import java.io.*;
    import java.nio.channels.*;

    public class UniqueApp
    {
    private String appName;
    private File file;
    private FileChannel channel;
    private FileLock lock;

    public UniqueApp(String appName) {
    this.appName = appName;
    }

    public boolean isAppActive() {
    try {
    file = new File(System.getProperty("user.home"), appName + ".tmp");
    channel = new RandomAccessFile(file, "rw").getChannel();

    try {
    lock = channel.tryLock();
    }
    catch (OverlappingFileLockException e) {
    closeLock();
    return true;
    }

    if (lock == null) {
    closeLock();
    return true;
    }

    Runtime.getRuntime().addShutdownHook(new Thread() {
    public void run() {
    closeLock();
    deleteFile();
    }
    });

    return false;
    }
    catch (Exception e) {
    closeLock();
    return true;
    }
    }

    private void closeLock() {
    try {
    lock.release();
    }
    catch (Exception e) {
    }
    try {
    channel.close();
    }
    catch (Exception e) {
    }
    }

    private void deleteFile() {
    try {
    file.delete();
    }
    catch (Exception e) {
    }
    }
    }
    --------------------------------------------------------------------------------

    And here is a simple test:

    --------------------------------------------------------------------------------
    public class UniqueAppTest {
    public static void main(String[] args) {
    new UniqueAppTest().test();
    }

    void test() {
    UniqueApp ua = new UniqueApp("MyAppId");

    if (ua.isAppActive()) {
    System.out.println("App is already active.");
    }
    else {
    System.out.println("App is NOT already active.");
    try {
    System.out.print("Hit <Enter> key to finish");
    System.in.read();
    }
    catch (Exception e) {
    }
    }
    }
    }
    --------------------------------------------------------------------------------
     
    Jean-Francois Briere, Sep 24, 2004
    #4
  5. Kent Yang

    FISH Guest


    It's a commonly used technique, as it allows the second instance to
    communicate with the first, passing over responsibility for opening
    any files set are arguments.


    -FISH- ><>
     
    FISH, Sep 24, 2004
    #5
  6. Kent Yang

    Jacob Guest

    I'll not add on to the fine answers already given.

    But just because something is technically possible
    doesn't necesserily mean it is a good idea. As a
    user I find this kind of program behaviour a pain in
    the neck. Often it is possible to change the application
    model/logic so it _can_ coexist with other instances;
    Possibly on the expence of programming effort.

    I don't say it is possible in your case, but please
    make sure you _try_ before you chose the simple
    way out :)
     
    Jacob, Sep 24, 2004
    #6
  7. Kent Yang

    Paul Lutus Guest

    There can be excellent reasons to do this. It is not necessarily a sign of
    prorgammer laziness. If the program reads and writes to its own
    configuration file, it is very confusing to a user to have multiple
    instances running, and it seems inconsistent to spend time reconfiguring an
    application, only to have the changes wiped out because of a minimized
    extra instance that is closed automatically when the system is shut down.

    For example, that is why my Web editor Arachnophilia, very
    user-configurable, uses this approach. Earler versions allowed multiple
    instances, and this was a disaster and a clear programming error. People
    would spend hours reconfiguring the user interface, only to see their work
    wiped out in a flash.

    This should not prevent multiple users from having their own instances, but
    it should prevent a single user from launching more than one.
     
    Paul Lutus, Sep 24, 2004
    #7
  8. Kent Yang

    steve Guest

    use a small server program. open a port. ( less than 20 lines of code)
    when you launch your app , check if you get an answer on the port, if so
    there is already an app running.
    if not then launch a small server.

    DO NOT use a flag file, if your app crashes or the disk gets corrupted, all
    sorts of things can happen. ( i have tired many different forms of flag files
    etc, but there is always one user who manages to beat the system, or corrupt
    it)

    With the server method, it always cleans up on the JVM exit, same with a
    crash.

    JustOne StartTracker = new JustOne();

    if (StartTracker.doit() == false) {
    try {
    .........
    program call routines
    }
    }
    else
    //it is already running so exit
    StartTracker.stopTask();
    System.exit(1);
    }





    package SimpleServer;

    import java.io.*;

    import java.net.*;


    //this package starts a java server listening on port 8181
    //it does this to stop the java program from being launched more than once
    // as the user only needs 1 copy running, also it causes client memory
    issues. if
    //more than 1 copy of the program is running
    public class SimpleServer extends Thread {
    public static final int port = 8181;
    ServerSocket serverSocket = null;
    Socket clientSocket = null;

    public void run() {
    try {
    // Create the server socket
    serverSocket = new ServerSocket(port, 1);

    while (true) {
    // Wait for a connection
    clientSocket = serverSocket.accept();

    // System.out.println("*** Got a connection! ");
    clientSocket.close();
    }
    } catch (IOException ioe) {
    System.out.println("Error in SimpleServer: " + ioe);
    System.exit(0); // we can do nothing else
    }
    }
    }


    package Server;

    import SimpleServer.*;

    import java.io.*;

    import java.net.*;


    //we have not started the error logger yet, ( we cannot, in case there is a
    second instance running
    //therefore we have to rely on the normal system errror caller
    public class JustOne {
    SimpleServer sds = null;
    boolean keepRunning = true;
    private boolean status = true; //say it is already running
    private Runnable r = null;

    //this is to be called when the program finally exits.
    public void stopTask() {
    keepRunning = false;
    }

    public boolean doit() {
    status = false; // say it is only the first copy

    try {
    Socket clientSocket = new Socket("localhost",
    SimpleServer.port);

    status = true;
    } catch (Exception e) {
    //this starts a new thread before the modal
    r = new Runnable() {
    public void run() {
    newServer();
    }
    };
    new Thread(r).start();
    status = false; // say it is only the first copy
    }

    // finally {
    return status;

    // }
    }

    public void newServer() {
    sds = new SimpleServer();
    sds.start();

    while (keepRunning == true) {
    try {
    // System.out.print(".");
    Thread.sleep(5 * 60);
    } catch (Exception e) {
    e.printStackTrace();
    System.exit(0); // we can do nothing else
    }
    }
    }
    }
     
    steve, Sep 24, 2004
    #8
  9. Kent Yang

    Jacob Guest

    All programs (of a certain size and complexity) access persistent
    session information. That a program has this feature is no excuse
    to make it a "singelton" application.

    Technically, session information is no different from _data_ (as
    stored in a database). If concurrent modification is an issue
    then your session storage strategy is to simple.

    But of course, the user runs a certain *risk* when launching
    several instances of a program that stores configuration. This
    is a risk that he is aware of and must take the consequences of.
    The user should understand that when changing color in app A, exiting
    app A (=> store settings), and then exiting app B (=> store settings),
    then the color setting will be lost for the next session. This
    will be the user responsibility. The programmer is responsible for
    ensuring that the session info is never corrupted it any way.
    It takes some effort.

    I have many standard programs running that solves this problem
    just fine, and I have written quite a few as well.
     
    Jacob, Sep 25, 2004
    #9
  10. Kent Yang

    Paul Lutus Guest

    This is not an excuse, this is a necessity. One program instance can store
    state information, end of story.
     
    Paul Lutus, Sep 25, 2004
    #10
  11. The simple server you presented does not allow two users
    (or two accounts) logged into one machine at the same time
    to use the application. You also need to consider that any
    chosen port may be used by another application. These
    problems can be solved, but I would still recommend a flag
    file. If the app won't run on less than Java 1.4, using a lock
    as mentioned elsewhere in this thread will eliminate the
    problem of a left-over flag file after a crash. Otherwise,
    just give the user the option of running anyway (with a
    stern warning to make sure the app is not already running)
    if the lock file is present.
     
    Larry Barowski, Sep 26, 2004
    #11
  12. Kent Yang

    steve Guest

    Jez.
    1. the port can be reconfigured.
    2. he did not request 2 users on the same machine, but to prevent multiple
    instances of the application from running.

    a simple call to "system properties " to get the user can fix this.


    allowing a user to run multiple instances "with a stern warning" is just
    stupid, It would require hte user to have intimate knowlege of the app , and
    if it is middleware, could cause a serious problem.

    flag files do not work reliably they rely on a non-real world situation
    sorry!!

    steve
     
    steve, Sep 27, 2004
    #12
  13. Kent Yang

    steve Guest

    you obviously have only ever written programs for Einstein like users.

    A recent (real) case to highlight this:

    A java program running on linux accessing an oracle database.
    program did not start fast enough, for said user. ( startup time about 10
    seconds)


    at about 5 seconds Said used clicked on program icon 20 times to make it go
    faster.

    multiple applications launched, java kicked in linux swap files , after 5
    minutes use switched off computer at power button, because it had "hung"

    result corrupted disk & corrupted flag files ( not cleaned up) + 20 unclosed
    connections to the oracle database.


    conclusions:

    1.A user can find a way to **** any system.
    2. Users are generally morons.

    Steve
     
    steve, Sep 27, 2004
    #13
  14. You missed my point. The code you provided was too simple, but
    you did not say "Of course, this is not sufficient to use in a real
    world program."
    He did not specify preventing multiple instances per system
    or per-user, so we can assume neither. When you presented
    a solution, you did not indicate that it was per-system.

    If this is an end-user application, then the goal is probably a
    single instance per-user. User-switching on XP means that
    even Windows-only apps must now handle multiple users
    per system.
    Well, sort of. You need some method of assigning different
    ports to different users.
    No, it's "just stupid" only if the result could be disastrous.
    If the result could be the application not remembering
    the high score for a video game, then it is quite
    reasonable.
    Using file locking (Java 1.4) on flag files is just as reliable
    as using sockets.
     
    Larry Barowski, Sep 27, 2004
    #14
  15. Kent Yang

    Kent Yang Guest

    Thanks everyone for all the great responses.

    My concern with socket approach is that one port will be dedicated to
    the application. I know its only one port / resource. The other
    concern is that I had socket applications that crash, and because it
    wasn't close properly, the socket can not be reused for a little while
    (probably because the OS resource not cleanup). So the port gets lock
    out until a system restart or until the OS catches up.

    The sample example posted by Jean-Francois Briere works great. I owe
    you a beer.

    It may sound like a strange request for this type of application
    feature but there are instances where this is beneficial to very basic
    users. Also, precedence have set by applications such as IDE(s),
    JBuilder for example. I am guessing they use some native code to
    bring up the current running instance.

    Thanks again everyone for all the great responses.

    Kent
     
    Kent Yang, Sep 28, 2004
    #15
  16. In practice, reusing a server port is never a problem.

    Even if the ServerSocket constructor doesn't set SO_REUSEADDR (i.e.
    setReuseAddress(true)) before binding the socket, you can. Although I
    believe it does by default.

    /gordon
     
    Gordon Beaton, Sep 28, 2004
    #16
  17. Kent Yang

    steve Guest

    O.K you obviously are set in your ways (as am I), and are determined to find
    every bone in the chicken egg
     
    steve, Sep 28, 2004
    #17
  18. Kent Yang

    Kyle Cordes Guest


    I use this approach, with very good results.

    There are two issues with the socket approach, which while they might not
    come up for some users, made that approach inappropriate for my project:

    1) Some "personal" firewall software will put up a warning message when a
    piece of software opens a listening socket, resultings in confused users and
    support calls

    2) Some customers like to deploy software using Terminal Services, Citrix,
    etc., where there can be many users on one machine; each needs to be able to
    run their own copy of the software, but none should be able to run more than
    one copy. This is automatic with the lock-file-in-home-dir approach, but
    with a listening socket, you'd need to find some way to have each user use a
    differnet listening socket.
     
    Kyle Cordes, Oct 1, 2004
    #18
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.