Запустить программу Java, если она еще не запущена
Мне нужно запустить 1-3 внешних программы в моем приложении Java, которые имеют пути, определенные пользователем. У меня мало требований:
-
Я не хочу, чтобы программа выполнялась, если она уже запущена
-
Я не хочу, чтобы какая-либо из программ, чтобы украсть фокус с моего приложения Java
-
Мне все равно, если кто-то из них не начнет или нет. Им просто нужно терпеть неудачу тихо.
Вот что я придумал до сих пор:
ProcessBuilder pb = new ProcessBuilder(userDefinedPath1);
try {
pb.start();
}
catch (Exception e) {
// Something went wrong, just ignore
}
И затем я повторяю это еще 3 раза с двумя другими путями. Это начинается, как я ожидал, и удовлетворяет моему третьему требованию просто отлично, но терпит неудачу в первых двух.
Каков наилучший способ сделать это?
Edit:
-
У меня нет контроля над этими другими приложениями. Они являются третьими лицами. Кроме того, они могли быть запущены или остановлены пользователем вручную в любое время.
-
Я знаю точные имена исполняемых файлов (например, "blah.exe" ), и они всегда будут одинаковыми, но пути к исполняемым файлам не обязательно будут.
-
Обертки пакетного файла здесь не возможны.
-
Другие приложения - это не java-приложения, а просто старые исполняемые файлы Windows.
Ответы
Ответ 1
Я предполагаю, что у вас нет контроля над двумя другими приложениями... Если бы вы это сделали, это было бы не так уж плохо - вы могли бы просто заставить их слушать сокет и посмотреть, доступен ли сокет когда вы придете.
Следующее решение может быть независимым от языка. Вы можете управлять всей системой через обертки пакетных файлов. Напишите командный файл, который создает файл при запуске и удаляет его, когда он останавливается. Unix-системы используют этот метод много - они чаще всего называют файл блокировки.
Если только ваше приложение когда-либо запустит эти другие приложения, вы можете просто отследить, если вы его запустили или нет, поэтому я предполагаю, что это невозможно, или вы не спросите, так что я предполагая, что пользователь мог запустить эти программы через какой-то другой механизм.
Если у вас нет контроля над запуском других приложений и вы даже не можете записать пакетный файл для их запуска, вы просто не сможете делать то, что хотите (обратите внимание, что приложения должны всегда использовать пакетный файл, даже если пользователь начал их вручную).
Я просто очень стараюсь, чтобы получить статус процесса и проанализировать его, но вы должны были бы точно знать, что другие приложения были вызваны в PS, это не совсем тривиально. Кроме того, все приложения Java имеют тенденцию иметь одну и ту же точную подпись в большинстве распечаток состояния процесса, которые могут сделать это бесполезным.
Проблема в том, что если одна из этих программ была запущена за пределами вашего приложения, у вас практически нет возможности идентифицировать этот факт, если вы не узнаете его точную сигнатуру статуса процесса, и даже тогда она flaky.
Ответ 2
Я предоставляю два ответа: один для 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 найден в системе, не запускайте. Если вы закончите, reset.
Настройки сохраняются в реестре Windows в HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs
Я рекомендую использовать блокировку файлов, чтобы убедиться, что приложение Java не запускается дважды в одно и то же время, потому что если программа сбой или зависание навсегда и будет убита, блокировка останется в противоречивом состоянии и, возможно, даже сможет выжить перезагрузка, которая вызовет проблемы, потому что, как программа узнает разницу между программой, которая все еще работает, и программой, которая потерпела крах и заблокировала заблокированный файл?
Ответ 3
Это то, что я сделал для решения проблемы, это простое решение для использования чистой 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();
}
Ответ 4
Я очень уверен, что это лучший способ; Приложение запускает "сервер" при запуске, и если порт сервера уже занят, приложение закрывается.
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);
}
}
Ответ 5
Если бы я столкнулся с этой проблемой в системе на основе unix, у меня возникло бы желание написать собственную функцию для сбора информации о процессе и попыток запуска внешних приложений. Это противоречит духу Java, но тип информации, которую вы пытаетесь собрать, и контроль запуска приложения находятся за пределами JVM.
Я не знаю достаточно о программировании Windows, чтобы указать вам в правильном направлении, но я полагаю, что есть вызовы Windows API, которые вы могли бы получить на языках .NET или на простом С++, которые могли бы помочь вам с вашим вторым критерием. Возможно, вы могли бы изменить вопрос, чтобы привлечь разработчиков, не связанных с Java, которые могли бы помочь.
Edit:
Отметьте Shell Execute Function в Windows API. Опция SW_SHOWNOACTIVATE позволяет текущему окну оставаться активным.
Ответ 6
См. Проверьте, запущена ли программа или процесс (Windows)
Этот совет будет определять, работает ли данная программа. Хорошо, это не очень из-за vbscript, но его легко реализовать.
Ответ 7
Есть одна небольшая вещь, которую вы можете сделать со всеми системами, и это включает в себя возможность блокировки файлов.
При запуске программы сначала создайте файл и заблокируйте его. Назовите это что-то вроде lockfile. Создайте временную метку и зашифруйте ее. Запишите это в это.
При запуске той же программы. Сначала проверьте наличие файла блокировки. Попытка заблокировать это. Игнорируйте вероятность того, что этот файл заблокирован. Прочитайте и проверьте отметку времени. Если timestamp слишком мала (например, 1 час назад), попробуйте запустить программу. Вы вернете временную метку назад, потому что она заблокирована. В этом случае предположим, что программа уже открыта.
При выходе из программы удалите этот файл блокировки.
Ответ 8
на windows xp (pro?) вы можете запустить команду "список задач" и проанализировать вывод, чтобы определить, запущен ли процесс.
вы можете использовать потоки, чтобы избежать каких-либо проблем с фокусом (я думаю)
Ответ 9
Вы можете использовать jps, чтобы узнать статус текущей java-программы.
jps - это инструмент Java, такой как javac, java и т.д.,