Запускайте Java-программу, только если она еще не запущена

StackOverflow https://stackoverflow.com/questions/660205

  •  20-08-2019
  •  | 
  •  

Вопрос

Мне нужно запустить 1-3 внешние программы в моем Java-приложении, у которых есть пути, определенные пользователем.У меня есть несколько требований:

  1. Я не хочу, чтобы программа выполнялась, если она уже запущена

  2. Я не хочу, чтобы какая-либо из программ крала фокус из моего Java-приложения

  3. Мне все равно, не запустится ли кто-нибудь из них или нет.Им просто нужно тихо потерпеть неудачу.

Вот что у меня получилось на данный момент:

ProcessBuilder pb = new ProcessBuilder(userDefinedPath1);
try {
    pb.start();
}
catch (Exception e) {
    // Something went wrong, just ignore
}

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

Каков наилучший способ сделать это?

Редактировать:

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

  2. Я знаю, что точный имена исполняемых файлов (например,"blah.exe ") и они будут всегда быть тем же самым, но пути к исполняемым файлам не обязательно будут такими.

  3. Обертки пакетных файлов здесь неосуществимы.

  4. Другими приложениями являются не java-приложения, просто обычные старые исполняемые файлы Windows.

Это было полезно?

Решение

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

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

Если только ваше приложение когда-либо запустит эти другие приложения, тогда вы могли бы просто отследить, запустили вы его или нет, поэтому я предполагаю, что это невозможно, иначе вы бы не спрашивали, поэтому я предполагаю, что пользователь, возможно, запустил эти программы с помощью какого-то другого механизма.

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

Я мог бы сделать самую последнюю попытку - получить статус процесса и проанализировать его, но вам нужно было бы точно знать, как вызывались другие приложения в PS, это на самом деле не тривиально.Кроме того, все приложения Java, как правило, имеют одинаковую точную подпись в большинстве распечаток состояния процесса, что может сделать это бесполезным.

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

Другие советы

Я даю два ответа, один для Linux:

Не запускайте программу, если она уже запущена, поместите это в файл под названием Main.java

import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

class JustOneLock {
  FileLock lock;
  FileChannel channel;

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

    lock = channel.tryLock();
    if (lock == null) {
      return true;
    }
    Runtime.getRuntime().addShutdownHook(new Thread() {
      public void run() {
        try {
          lock.release();
          channel.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
    return false;
  }
}

public class Main {
  public static void main(String[] args)throws Exception {
    JustOneLock u = new JustOneLock();

    if (u.isAppActive()) {
      System.out.println("Already active, stop!");
      System.exit(1);
    }
    else {
      System.out.println("NOT active... Do hard work for 5 seconds.");
      try{Thread.sleep(5000);}catch(Exception e){}
    }
  }
}

Скомпилируйте его и запустите.Затем откройте новый терминал и попытайтесь запустить его снова, пока другой запущен, но он не сработает.

Еще один ответ для Windows

Эта программа не позволит запустить себя, если она уже запущена в текущей системе.Это предназначено только для систем Windows.

import java.io.*;
import java.util.prefs.Preferences;

public class JavaApplication3 {

    public static void main(String[] args){
        if(isRunning()){
            System.out.println("Two instances of this program cannot " +
                    "be running at the same time.  Exiting now");
        }
        else{
            onStart();
            epicHeavyWorkGoesHere();
            onFinish();
        }
    }
    public static void epicHeavyWorkGoesHere(){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException ex) {}
    }
    public static void onStart(){
        Preferences prefs = Preferences.systemRoot().node("JavaApplication3");
        prefs.put("RUNNINGPID", getCurrentPID());
    }
    public static void onFinish(){
        Preferences prefs = Preferences.systemRoot().node("JavaApplication3");
        prefs.put("RUNNINGPID", "");
    }
    public static boolean isRunning(){
        Preferences prefs = Preferences.systemRoot().node("JavaApplication3");

        if (prefs.get("RUNNINGPID", null) == null || prefs.get("RUNNINGPID", null).equals(""))
            return false;

        if (isProcessIdRunningOnWindows(Integer.parseInt(prefs.get("RUNNINGPID", null))))
            return true;
        return false;
    }
    public static String getCurrentPID(){
        //This function is designed to get the PID from the windows system, it may
        //not work for Linux or Mac.  You'll have to acquire a suitable getCurrentPID function
        try{
            java.lang.management.RuntimeMXBean runtime = java.lang.management.ManagementFactory.getRuntimeMXBean();
            java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
            jvm.setAccessible(true);
            sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
            java.lang.reflect.Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
            pid_method.setAccessible(true);
            return pid_method.invoke(mgmt) + "";
        }
        catch(Exception e){
            throw new RuntimeException("Cannot get the current PID");
        }
    }
    public static boolean isProcessIdRunningOnWindows(int pid){
        //This Function only works for windows, if you want it to work on linux
        //you will have to go find a replacement method that takes the processID
        //as a parameter and spits out a true/false if it is running on the system.
        try {
            Runtime runtime = Runtime.getRuntime();
            String cmds[] = {"cmd", "/c", "tasklist /FI \"PID eq " + pid + "\""};
            Process proc = runtime.exec(cmds);

            InputStream inputstream = proc.getInputStream();
            InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
            BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
            String line;
            while ((line = bufferedreader.readLine()) != null) {
                if (line.contains(" " + pid + " ")){
                    return true;
                }
            }
            return false;
        }
        catch (Exception ex) {
            throw new RuntimeException("Cannot run the tasklist command to query if a pid is running or not");
        }
    }
}

Стратегия приведенного выше кода заключается в том, чтобы сохранить PID с момента последнего запуска, если этот PID обнаружен запущенным в системе, не запускайте.Если вы закончите, сбросьте настройки.

Настройки хранятся в реестре Windows в HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs

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

Я очень уверен, что это лучший способ;Приложение запускает "сервер" при запуске, и если порт сервера уже занят, приложение закрывается.

public static ServerSocket ss;

public static void main (String[] args) {

    ss = null;

    try {
        ss = new ServerSocket(1044);
    } catch (IOException e) {
        System.err.println("Application already running!");
        System.exit(-1);
    }
}

Это то, что я сделал, чтобы решить проблему, это простое решение для использования чистой java:

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

public void main(String[] args){
    if(isRunning()){
        JOptionPane.showMessageDialog(this, "2 instances of this program cannot be running at the same time. \n Exiting now");
        System.exit(0);
    }else{
        onStart();
    }
}    

public final void onStart(){
    Preferences prefs;
    prefs = Preferences.userRoot().node(this.getClass().getName());
    prefs.put("RUNNING", "true");
}

public final void onFinish(){
    Preferences prefs;
    prefs = Preferences.userRoot().node(this.getClass().getName());
    prefs.put("RUNNING", "false");
}

public boolean isRunning(){
    Preferences prefs;
    prefs = Preferences.userRoot().node(this.getClass().getName());
    return prefs.get("RUNNING", null) != null ? Boolean.valueOf(prefs.get("RUNNING", null)) : false;
}

private void formWindowClosing(java.awt.event.WindowEvent evt) {
    onFinish();
}

Видишь Проверьте, запущена ли программа или процесс (Windows)

Этот совет определит, запущена ли данная программа.Хорошо, это некрасиво из-за vbscript, но это легко реализовать.

Если бы я имел дело с этой проблемой в системе на базе unix, у меня был бы соблазн написать встроенную функцию для сбора информации о процессе и попытки запуска внешних приложений.Это противоречит духу Java, но тип информации, которую вы пытаетесь собрать, и управление запуском приложения находятся за пределами JVM.

Я недостаточно разбираюсь в программировании Windows, чтобы указать вам правильное направление, но я полагаю, что есть вызовы Windows API, к которым вы могли бы получить доступ на языках .NET или обычном C ++, которые могли бы помочь вам со вторым критерием.Возможно, вы могли бы изменить вопрос, чтобы привлечь разработчиков, не связанных с Java, которые могли бы помочь.


Редактировать:

Ознакомьтесь с Функция выполнения оболочки из Windows API.Появляется опция SW_SHOWNOACTIVATE, позволяющая текущему окну оставаться активным.

Есть одна незначительная вещь, которую вы можете сделать со всеми системами, и это включает в себя возможность блокировки файлов.

При запуске программы Сначала создайте файл и заблокируйте его.Назовите это как-нибудь вроде lockfile .Сгенерируйте временную метку и зашифруйте ее.Запишите это в него.

В той же программе запустите Сначала проверьте, присутствует ли файл блокировки.Попытайтесь заблокировать его.Игнорируйте возможность того, что этот файл заблокирован.Прочитайте и проверьте временную метку.Если отметка времени является чем-то незначительно малым (например, 1 час назад), попытайтесь запустить программу.Вы будете ошибочно записывать временную метку обратно, так как она заблокирована.В этом случае предположим, что программа уже открыта.

При выходе из программы удалите этот файл блокировки.

в Windows xp (pro?) вы можете запустить команду "список задач" и проанализировать выходные данные, чтобы определить, запущен ли процесс.вы можете использовать потоки, чтобы избежать любых проблем с фокусом (я думаю)

Вы можете использовать jps, чтобы узнать состояние запущенной java-программы.jps - это один из инструментов Java, таких как javac, java и т.д.,

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top