Как разрешить запуск только одного экземпляра Java-программы за раз?

Мне нужно запретить пользователям запускать Java-приложение (приложение WebStart Swing) несколько раз. Поэтому, если приложение уже запущено, не должно быть возможности запустить его снова или снова показать предупреждение/быть закрытым.

Есть ли какой-нибудь удобный способ достичь этого? Я думал о блокировке порта или записи sth в файл. Но, надеюсь, вы можете получить доступ к некоторым свойствам системы или JVM?

кстати. целевая платформа - Windows XP с Java 1.5

Ответ 1

Я думаю, что ваше предложение открыть порт для прослушивания при запуске приложения - лучшая идея.

Это очень легко сделать, и вам не нужно беспокоиться о его очистке при закрытии приложения. Например, если вы пишете файл, но кто-то тогда убивает процессы с помощью диспетчера задач, файл не будет удален.

Кроме того, если я правильно помню, нет простого способа получить PID процесса Java из JVM, поэтому не пытайтесь сформулировать решение с использованием PID.

Что-то вроде этого должно сделать трюк:

private static final int PORT = 9999;
private static ServerSocket socket;    

private static void checkIfRunning() {
  try {
    //Bind to localhost adapter with a zero connection queue 
    socket = new ServerSocket(PORT,0,InetAddress.getByAddress(new byte[] {127,0,0,1}));
  }
  catch (BindException e) {
    System.err.println("Already running.");
    System.exit(1);
  }
  catch (IOException e) {
    System.err.println("Unexpected error.");
    e.printStackTrace();
    System.exit(2);
  }
}

Этот пример кода явно привязывается к 127.0.0.1, который должен избегать любых предупреждений о брандмауэре, поскольку любой трафик по этому адресу должен быть из локальной системы.

При выборе порта старайтесь избегать упоминания в списке списка известных портов. В идеале вы должны использовать порт, который можно настроить в файле или через переключатель командной строки в случае конфликтов.

Ответ 2

Поскольку в вопросе говорится, что WebStart используется, очевидным решением является использование javax.jnlp.SingleInstanceService.

Эта услуга доступна в версии 1.5. Обратите внимание, что в настоящее время 1,5 в течение всего срока службы заканчивается. Получить с Java SE 6!

Ответ 3

Я думаю, что лучшей идеей было бы использовать блокировку файлов (довольно старая идея:)). Начиная с Java 1.4 была введена новая библиотека ввода-вывода, которая позволяет блокировать файлы.

После запуска приложения он пытается получить блокировку файла (или создать его, если его не существует), когда приложение выходит из блокировки. Если приложение не может получить блокировку, оно завершает работу.

Например, как сделать блокировку файлов, например, в Java Developers Almanac.

Если вы хотите использовать блокировку файлов в приложении Java Web Start или апплета, вам нужно спеть приложение или апплет.

Ответ 4

Мы делаем то же самое в С++, создавая объект mutex ядра и ищем его при запуске. Преимущества такие же, как при использовании сокета, т.е. Когда процесс замирает/вылетает/выезжает/, объект mutex очищается ядром.

Я не программист на Java, поэтому я не уверен, что вы можете делать то же самое в Java?

Ответ 7

Вы можете использовать реестр, хотя это беззаботно проигрывает цели использования языка высокого уровня, такого как java. По крайней мере, ваша целевая платформа: windows = D

Ответ 8

Попробуйте JUnique:

String appId = "com.example.win.run.main";
boolean alreadyRunning;
try {
    JUnique.acquireLock(appId);
    alreadyRunning = false;
} catch (AlreadyLockedException e) {
    alreadyRunning = true;
}
if (alreadyRunning) {
    Sysout("An Instance of this app is already running");
    System.exit(1);
}

Ответ 9

Я видел так много этих вопросов, и я искал решение той же проблемы независимо от платформы, которая не позволяет столкнуться с брандмауэрами или попасть в сокет.

Итак, вот что я сделал:

import java.io.File;
import java.io.IOException;

/**
 * This static class is in charge of file-locking the program
 * so no more than one instance can be run at the same time.
 * @author nirei
 */
public class SingleInstanceLock {

    private static final String LOCK_FILEPATH = System.getProperty("java.io.tmpdir") + File.separator + "lector.lock";
    private static final File lock = new File(LOCK_FILEPATH);
    private static boolean locked = false;

    private SingleInstanceLock() {}

    /**
     * Creates the lock file if it not present and requests its deletion on
     * program termination or informs that the program is already running if
     * that the case.
     * @return true - if the operation was succesful or if the program already has the lock.<br>
     * false - if the program is already running
     * @throws IOException if the lock file cannot be created.
     */
    public static boolean lock() throws IOException {
        if(locked) return true;

        if(lock.exists()) return false;

        lock.createNewFile();
        lock.deleteOnExit();
        locked = true;
        return true;
    }
}

Использование System.getProperty( "java.io.tmpdir" ) для пути lockfile гарантирует, что вы всегда будете создавать свою блокировку в одном месте.

Затем из вашей программы вы просто вызываете что-то вроде:

blah blah main(blah blah blah) {
    try() {
        if(!SingleInstanceLock.lock()) {
            System.out.println("The program is already running");
            System.exit(0);
        }
    } catch (IOException e) {
        System.err.println("Couldn't create lock file or w/e");
        System.exit(1);
    }
}

И это делает это для меня. Теперь, если вы убьете программу, он не удалит файл блокировки, но вы можете решить эту проблему, написав ПИД-код программы в файле блокировки и сделав метод lock(), если этот процесс уже запущен. Это оставлено как предположение для всех, кого это интересует.:)