LiveHelp
:: О компании :: Портфолио :: Основные услуги :: Дополнительные услуги :: Информация

Запрет запуска второго экземпляра приложения на Java

Очень часто при разработке ПО требуется сделать так, что бы в один момент времени была запущен только один экземпляр приложения. В некоторых языках программирования в объектах синхронизации реализована такая возможность. В Java нативные библиотеки не предоставляют подобного функционала. В интернете есть информация о 2х основных подходах для реализации данной задачи: с использованием лока на файл или локом порта. Поскольку доступные примеры кода не достаточно хороши я сделал свою реализацию блока запуска более одного экземпляра приложения.

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

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

Я решил это таким способом: во временной директории создается файл и на него устанавливается Lock. Имя файла генерируется на основании ключа, который передается при создании лока. Другие запущенные экземпляры программы (либо другие программы использующие тот же ключ лока) проверяют есть этот файл в директории и установлен ли на него Lock.

Использование моего класса очень простое.

try {
    // Устанавливаем LOCK //
    if (!AppLock.setLock("MY_CUSTOM_LOCK_KEY")) {
        throw new RuntimeException("Only one application instance may run at the same time!");
    }

    // YOUR CODE
}
finally{
    AppLock.releaseLock(); // Убираем лок
}

Сам класс AppLock работает только через 2 статических метода: setLock и releaseLock. Думаю, что у вас не возникнет особых проблем с его использованием. Этот код кроссплатформенный, я проверял его работу на Windows и Linux.

import java.io.File;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * AppLock
 * @author http://fresh2L.com
 * @version 1.0
 */

public class AppLock
{

  /** Закрытый конструктор */
  private AppLock() { }

  /**
   * Файл на который цепляется Lock
   */

  File lock_file = null;

  FileLock lock = null;

  FileChannel lock_channel = null;

  FileOutputStream lock_stream = null;

  /**
   * Создает экземпляр класса
   * @param key Ключ программы - пользовательское значение
   */

  private AppLock(String key) throws Exception
  {
    // TODO: Может быть как-то переменную key проверять на валидность т.к. из нее имя файла может формироваться ?
    String tmp_dir = System.getProperty("java.io.tmpdir");
    if (!tmp_dir.endsWith(System.getProperty("file.separator"))) {
      tmp_dir += System.getProperty("file.separator");
    }
   
    // Получаем MD5 ключа и создаем классный URI
    try {
      java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
      md.reset();
      String hash_text = new java.math.BigInteger(1, md.digest(key.getBytes())).toString(16);
      // Как правило нули в начале строки урезаются этот код добавляет нули обратно
      while (hash_text.length() < 32) {
        hash_text = "0" + hash_text;
      }
      lock_file = new File(tmp_dir + hash_text + ".app_lock");
    }
    catch (Exception ex) {
    }

    // Если глюкнул MD5 попробуем просто так создать URI на основе ключа
    if (lock_file == null) {
        lock_file = new File(tmp_dir + key + ".app_lock");
    }

    lock_stream = new FileOutputStream(lock_file);

    String f_content = "Java AppLock Object\r\nLocked by key: " + key + "\r\n";
    lock_stream.write(f_content.getBytes());

    lock_channel = lock_stream.getChannel();

    lock = lock_channel.tryLock();

    if (lock == null) {
      throw new Exception("Can't create Lock");
    }
  }

  /**
   * Убираем Lock. Теперь другое приложение сможет его заново установить.
   * @throws Throwable
   */

  private void release() throws Throwable
  {
    if (lock != null) {
      lock.release();
    }

    if (lock_channel != null) {
      lock_channel.close();
    }

    if (lock_stream != null) {
      lock_stream.close();
    }

    if (lock_file != null) {
      lock_file.delete();
    }
  }

  @Override
  protected void finalize() throws Throwable
  {
    this.release();
    super.finalize();
  }

  private static AppLock instance;

  /**
   * Устанавливает Lock с указанным ключем
   * Метод может вызываться только один раз, повторные вызовы игнорируются.
   *
   * @param key Ключ блока
   */

  public static boolean setLock(String key)
  {
    if (instance != null) {
      return true;
    }

    try {
      instance = new AppLock(key);
    }
    catch (Exception ex) {
      instance = null;
      Logger.getLogger(Application.class.getName()).log(Level.SEVERE, "Обламался AppLock", ex);
      return false;
    }

    Runtime.getRuntime().addShutdownHook(new Thread()
    {
      @Override
      public void run()
      {
        AppLock.releaseLock();
      }
    });
    return true;
  }

  /**
   * Как бы убирает Lock.
   * После вызова повторое использование AppLock будет невозможно.
   */

  public static void releaseLock()
  {
    if (instance == null) {
      return;
    }
    try {
      instance.release();
    }
    catch (Throwable ex) {
      Logger.getLogger(AppLock.class.getName()).log(Level.SEVERE, null, ex);
    }
  }
}
Назойливая реклама:

Многие сейчас не знают как зарегистрироваться в контакте, а все потому, что эта социальная сеть недавно ввела новые правила регистрации пользователей в системе. Кто не успел - тот опоздал.

Share this

Мы на других сайтах

F P V