Java AWT uygulamasını nasıl yeniden başlatabilirim? Bir olay işleyicisi eklediğim bir düğmem var. Uygulamayı yeniden başlatmak için hangi kodu kullanmalıyım?
Application.Restart()
Bir C # uygulamasında yaptığım şeyi yapmak istiyorum .
Java AWT uygulamasını nasıl yeniden başlatabilirim? Bir olay işleyicisi eklediğim bir düğmem var. Uygulamayı yeniden başlatmak için hangi kodu kullanmalıyım?
Application.Restart()
Bir C # uygulamasında yaptığım şeyi yapmak istiyorum .
Yanıtlar:
Elbette bir Java uygulamasını yeniden başlatmak mümkündür.
Aşağıdaki yöntem, bir Java uygulamasını yeniden başlatmanın bir yolunu gösterir:
public void restartApplication()
{
final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
final File currentJar = new File(MyClassInTheJar.class.getProtectionDomain().getCodeSource().getLocation().toURI());
/* is it a jar file? */
if(!currentJar.getName().endsWith(".jar"))
return;
/* Build command: java -jar application.jar */
final ArrayList<String> command = new ArrayList<String>();
command.add(javaBin);
command.add("-jar");
command.add(currentJar.getPath());
final ProcessBuilder builder = new ProcessBuilder(command);
builder.start();
System.exit(0);
}
Temel olarak şunları yapar:
MyClassInTheJar
kavanoz konumunu bulmak için sınıfı kullanarak bir kavanoz)System.exit(0)
süreci sonlandırıp sonlandırmayacağını sormak, bu cevabın gerçekten işe yarayıp yaramadığıyla aynı cevaba sahiptir ve neden. Cevabınızla birlikte mantıklı bir açıklama yapamıyorsanız, kötü bir iş çıkardınız. Cevap verdiğinden daha fazla soru sağlayan cevap, kapsamlı bir cevap örneği değildir. İyi cevaplar sadece kodu göstermekle kalmaz, aynı zamanda nasıl ve neden çalıştıklarını, dezavantajların ve alternatiflerin neler olduğunu da açıklar. Bunları örtmeye çalışmadın bile.
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
StringBuilder cmd = new StringBuilder();
cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java ");
for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
cmd.append(jvmArg + " ");
}
cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" ");
cmd.append(Main.class.getName()).append(" ");
for (String arg : args) {
cmd.append(arg).append(" ");
}
Runtime.getRuntime().exec(cmd.toString());
System.exit(0);
}
}
İmkansız olduğunu söyleyenlere adanmıştır.
Bu program, orijinal komut satırını yeniden oluşturmak için mevcut tüm bilgileri toplar. Daha sonra onu çalıştırır ve aynı komut olduğu için uygulamanız ikinci kez başlar. Sonra orijinal programdan çıkıyoruz, alt program çalışmaya devam ediyor (Linux altında bile) ve aynı şeyi yapıyor.
UYARI : Bunu çalıştırırsanız, çatal bombasına benzer şekilde yeni süreçler oluşturmayı asla bitirmeyeceğini unutmayın .
ManagementFactory.getRuntimeMXBean().getInputArguments()
, size yalnızca JVM'ye aktarılan giriş bağımsız değişkenlerini verecektir. Uygulamanıza aktarılan parametreleri kaçırıyor. örneğin java -jar start.jar -MISSED_PARAM=true
,. Bir oracle jvm'de, kullanarak bu parametreleri alabilirsiniz System.getProperty("sun.java.command")
.
ProcessBuilder
ve inheritIO()
, çocuk VM ana VM son vereceği şekilde başlatılabilir.
Temel olarak, yapamazsınız. En azından güvenilir bir şekilde değil. Ancak buna gerek yok.
Bir Java programını yeniden başlatmak için JVM'yi yeniden başlatmanız gerekir. JVM'yi yeniden başlatmak için yapmanız gerekenler
java
Kullanılan başlatıcıyı bulun . Deneyebilirsiniz, System.getProperty("java.home")
ancak bunun uygulamanızı başlatmak için kullanılan başlatıcıya işaret edeceğine dair bir garanti yoktur. (Döndürülen değer , uygulamayı başlatmak için kullanılan JRE'yi göstermeyebilir veya tarafından geçersiz kılınabilir -Djava.home
.)
Sen muhtemelen ayarları vb (orijinal anmak isterim -Xmx
, -Xms
kullandığınız ayarlar ilk JVM başlatmak için hangi rakam gerekir, ...) bu yüzden. Kullanmayı deneyebilirsiniz, ManagementFactory.getRuntimeMXBean().getInputArguments()
ancak bunun kullanılan ayarları yansıtacağının garantisi yoktur. Bu, o yöntemin belgelerinde bile belirtilmiştir :
Genellikle, 'java' komutunun tüm komut satırı seçenekleri Java sanal makinesine aktarılmaz. Bu nedenle, döndürülen girdi bağımsız değişkenleri tüm komut satırı seçeneklerini içermeyebilir.
Programınız Standard.in
orijinal stdin'den girdi okursa , yeniden başlatmada kaybolacaktır.
Bu hilelerin ve bilgisayar korsanlarının çoğu, bir SecurityManager
.
Uygulamanızı, her şeyi temizlemeyi kolaylaştıracak şekilde tasarlamanızı ve ardından "ana" sınıfınızın yeni bir örneğini oluşturmanızı öneririm.
Çoğu uygulama, ana yöntemde bir örnek oluşturmaktan başka hiçbir şey yapmayacak şekilde tasarlanmıştır:
public class MainClass {
...
public static void main(String[] args) {
new MainClass().launch();
}
...
}
Bu kalıbı kullanarak, aşağıdaki gibi bir şey yapmak yeterince kolay olmalıdır:
public class MainClass {
...
public static void main(String[] args) {
boolean restart;
do {
restart = new MainClass().launch();
} while (restart);
}
...
}
ve launch()
sadece ve ancak uygulama yeniden başlatılması gereken bir şekilde kapatılmışsa true döndürelim.
Açıkçası, bir Java programı kendisini yeniden başlatamaz, çünkü bunun için çalıştığı JVM'yi öldürmesi ve ardından yeniden başlatması gerekir, ancak JVM artık çalışmadığında (öldürüldüğünde) herhangi bir işlem yapılamaz.
AWT bileşenlerini yeniden yüklemek, paketlemek ve başlatmak için özel sınıf yükleyicilerle bazı hileler yapabilirsiniz, ancak bu GUI olay döngüsü ile ilgili olarak büyük olasılıkla birçok baş ağrısına neden olacaktır.
Uygulamanın nasıl başlatıldığına bağlı olarak, JVM belirli bir kodla çıkarken devam eden bir do / while döngüsü içeren bir sarmalayıcı komut dosyasında JVM'yi başlatabilir, ardından AWT uygulamasının çağırması gerekir System.exit(RESTART_CODE)
. Örneğin sözde kod komut dosyası oluşturmada:
DO
# Launch the awt program
EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)
AWT uygulaması, yeniden başlatma gerektirmeyen "normal" sonlandırmada RESTART_CODE dışında bir şeyle JVM'den çıkmalıdır.
JavaApplicationStub
... Bunun kolay bir yolu olup olmadığından emin değilim.
Eclipse genellikle bir eklenti yüklendikten sonra yeniden başlatılır. Bunu, Windows için bir sarmalayıcı eclipse.exe (başlatıcı uygulaması) kullanarak yaparlar. Bu uygulama, çekirdek tutulma çalıştırıcı kavanozunu çalıştırır ve eğer eclipse java uygulaması bir yeniden başlatma koduyla sona ererse, eclipse.exe tezgahı yeniden başlatır. Yeniden başlatma işlemini gerçekleştirmek için benzer bir yerel kod parçası, kabuk betiği veya başka bir java kodu sarıcı oluşturabilirsiniz.
pencereler
public void restartApp(){
// This launches a new instance of application dirctly,
// remember to add some sleep to the start of the cmd file to make sure current instance is
// completely terminated, otherwise 2 instances of the application can overlap causing strange
// things:)
new ProcessBuilder("cmd","/c start /min c:/path/to/script/that/launches/my/application.cmd ^& exit").start();
System.exit(0);
}
/ min komut dosyasını simge durumuna küçültülmüş pencerede başlatmak için
^ & bitirdikten sonra cmd penceresini kapatmak için çık
örnek bir cmd komut dosyası olabilir
@echo off
rem add some sleep (e.g. 10 seconds) to allow the preceding application instance to release any open resources (like ports) and exit gracefully, otherwise the new instance could fail to start
sleep 10
set path=C:\someFolder\application_lib\libs;%path%
java -jar application.jar
Uyku 10 10 saniye boyunca uyku
Uygulamanızı gerçekten yeniden başlatmanız gerekiyorsa, başlangıçta ayrı bir uygulama yazabilirsiniz ...
Bu sayfa, farklı senaryolar için birçok farklı örnek sunar:
Bu soru eski ve yanıtlanmış olmasına rağmen, bazı çözümlerle ilgili bir sorunla karşılaştım ve önerimi karışıma eklemeye karar verdim.
Bazı çözümlerle ilgili sorun, tek bir komut dizesi oluşturmalarıdır. Bu, özellikle java.home gibi bazı parametreler boşluk içerdiğinde sorun yaratır .
Örneğin, pencerelerde çizgi
final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
Bunun gibi bir şey döndürebilir:C:\Program Files\Java\jre7\bin\java
Bu dizge tırnak işaretleri arasına alınmalı veya içindeki boşluk nedeniyle kaçmalı Program Files
. Çok büyük bir sorun değil, ancak özellikle çapraz platform uygulamalarında biraz can sıkıcı ve hataya açık.
Bu nedenle benim çözümüm komutu bir komut dizisi olarak oluşturur :
public static void restart(String[] args) {
ArrayList<String> commands = new ArrayList<String>(4 + jvmArgs.size() + args.length);
List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
// Java
commands.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
// Jvm arguments
for (String jvmArg : jvmArgs) {
commands.add(jvmArg);
}
// Classpath
commands.add("-cp");
commands.add(ManagementFactory.getRuntimeMXBean().getClassPath());
// Class to be executed
commands.add(BGAgent.class.getName());
// Command line arguments
for (String arg : args) {
commands.add(arg);
}
File workingDir = null; // Null working dir means that the child uses the same working directory
String[] env = null; // Null env means that the child uses the same environment
String[] commandArray = new String[commands.size()];
commandArray = commands.toArray(commandArray);
try {
Runtime.getRuntime().exec(commandArray, env, workingDir);
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
Sadece diğer cevaplarda bulunmayan bilgileri eklemek.
/proc/self/cmdline
kullanılabilirProcfs sağlayan bir ortamda çalışıyorsanız ve bu nedenle /proc
dosya sistemine sahipseniz (bu taşınabilir bir çözüm olmadığı anlamına gelir), Java'nın /proc/self/cmdline
kendisini yeniden başlatmak için aşağıdaki gibi okumasını sağlayabilirsiniz:
public static void restart() throws IOException {
new ProcessBuilder(getMyOwnCmdLine()).inheritIO().start();
}
public static String[] getMyOwnCmdLine() throws IOException {
return readFirstLine("/proc/self/cmdline").split("\u0000");
}
public static String readFirstLine(final String filename) throws IOException {
try (final BufferedReader in = new BufferedReader(new FileReader(filename))) {
return in.readLine();
}
}
/proc/self/cmdline
Mevcut olan sistemlerde , bu muhtemelen mevcut Java sürecini Java'dan "yeniden başlatmanın" en zarif yoludur. JNI dahil değildir ve yolların ve malzemelerin tahmin edilmesine gerek yoktur. Bu aynı zamanda ikiliye aktarılan tüm JVM opsiyonlarını da halleder java
. Komut satırı, mevcut JVM işleminden biriyle tam olarak aynı olacaktır.
Günümüzde GNU / Linux (Android dahil) dahil olmak üzere birçok UNIX sisteminde procfs bulunmaktadır. Ancak FreeBSD gibi bazılarında, kullanımdan kaldırılmıştır ve aşamalı olarak kaldırılmaktadır. Mac OS X , procfs içermemesi anlamında bir istisnadır . Windows'da da procfs yoktur . Cygwin'in procf'leri vardır, ancak Java için görünmezdir çünkü yalnızca Windows sistem çağrıları yerine Cygwin DLL'leri kullanan uygulamalar tarafından görülebilir ve Java , Cygwin'in farkında değildir.
ProcessBuilder.inheritIO()
Varsayılan, başlatılan İşlemin stdin
/ stdout
/ stderr
(Java'da System.in
/ System.out
/ olarak adlandırılır System.err
), o anda çalışan işlemin yeni başlatılan işlemle iletişim kurmasını sağlayan borulara ayarlanmasıdır . Mevcut işlemi yeniden başlatmak istiyorsanız, büyük olasılıkla istediğiniz bu değildir . Bunun yerine, stdin
/ stdout
/ ' stderr
nin mevcut VM'ninkilerle aynı olmasını istersiniz . Buna kalıtsal denir . Sen arayarak yapabilirsiniz inheritIO()
çıkartmalarınızın ProcessBuilder
örneği.
Bir restart()
işlevin sık kullanım durumu, bir güncellemeden sonra uygulamayı yeniden başlatmaktır. Bunu Windows'ta en son denediğimde sorunluydu. Uygulama .jar
dosyasının üzerine yeni sürümü yazdığında, uygulama hatalı davranmaya ve .jar
dosya hakkında istisnalar vermeye başladı . Sadece söylüyorum, kullanım durumunuz buysa. O zamanlar, uygulamayı bir toplu iş dosyasına sararak ve toplu iş dosyasında System.exit()
sorguladığım ve bunun yerine toplu iş dosyasının uygulamayı yeniden başlattığından sihirli bir dönüş değeri kullanarak sorunu çözdüm.
Bu soruyla karşılaştığımda konuyu kendim araştırıyordum.
Cevabın zaten kabul edilmiş olmasına bakılmaksızın, yine de eksiksizlik için alternatif bir yaklaşım önermek istiyorum. Özellikle, Apache Ant çok esnek bir çözüm olarak hizmet etti.
Temelde, her şeyi tek bir Java yürütme görev ile bir Ant komut dosyası aşağı kaynar (bakınız burada ve burada bir Java kodundan çağrılan) (bkz burada ). Bir yöntem başlatması olabilen bu Java kodu, uygulamanın yeniden başlatılması gereken bir parçası olabilir. Uygulamanın Apache Ant kitaplığına (jar) bağımlı olması gerekir.
Uygulamanın yeniden başlatılması gerektiğinde, yöntemi başlatmalı ve VM'den çıkmalıdır . Ant java görevinde seçenekler çatalı olmalı ve spawn true olarak ayarlanmalıdır.
İşte bir Ant betiği örneği:
<project name="applaucher" default="launch" basedir=".">
<target name="launch">
<java classname="package.MasinClass" fork="true" spawn="true">
<jvmarg value="-splash:splash.jpg"/>
<jvmarg value="-D other VM params"/>
<classpath>
<pathelement location="lib-1.jar" />
...
<pathelement location="lib-n.jar" />
</classpath>
</java>
</target>
</project>
Başlatma yönteminin kodu aşağıdaki gibi görünebilir:
public final void launch(final String antScriptFile) {
/* configure Ant and execute the task */
final File buildFile = new File(antScriptFile);
final Project p = new Project();
p.setUserProperty("ant.file", buildFile.getAbsolutePath());
final DefaultLogger consoleLogger = new DefaultLogger();
consoleLogger.setErrorPrintStream(System.err);
consoleLogger.setOutputPrintStream(System.out);
consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
p.addBuildListener(consoleLogger);
try {
p.fireBuildStarted();
p.init();
final ProjectHelper helper = ProjectHelper.getProjectHelper();
p.addReference("ant.projectHelper", helper);
helper.parse(p, buildFile);
p.executeTarget(p.getDefaultTarget());
p.fireBuildFinished(null);
} catch (final BuildException e) {
p.fireBuildFinished(e);
}
/* exit the current VM */
System.exit(0);
}
Burada çok uygun bir şey, aynı komut dosyasının ilk uygulama başlangıcı ve yeniden başlatmalar için kullanılmasıdır.
Eski soru ve hepsi. Ancak bu, bazı avantajlar sunan başka bir yoldur.
Windows'ta, görev zamanlayıcıdan uygulamanızı sizin için yeniden başlatmasını isteyebilirsiniz. Bu, uygulama yeniden başlatılmadan önce belirli bir süre bekleme avantajına sahiptir. Görev yöneticisine gidip görevi silebilirsiniz ve görev tekrarlamayı durdurur.
SimpleDateFormat hhmm = new SimpleDateFormat("kk:mm");
Calendar aCal = Calendar.getInstance();
aCal.add(Calendar.SECOND, 65);
String nextMinute = hhmm.format(aCal.getTime()); //Task Scheduler Doesn't accept seconds and won't do current minute.
String[] create = {"c:\\windows\\system32\\schtasks.exe", "/CREATE", "/F", "/TN", "RestartMyProg", "/SC", "ONCE", "/ST", nextMinute, "/TR", "java -jar c:\\my\\dev\\RestartTest.jar"};
Process proc = Runtime.getRuntime().exec(create, null, null);
System.out.println("Exit Now");
try {Thread.sleep(1000);} catch (Exception e){} // just so you can see it better
System.exit(0);
Yoda'nın ' geliştirilmiş ' cevabına benzer, ancak daha fazla iyileştirmelerle (hem işlevsel, hem okunabilirlik hem de test edilebilirlik). Artık çalıştırmak güvenlidir ve verilen program argümanlarının miktarı kadar yeniden başlatılır.
JAVA_TOOL_OPTIONS
Seçenek birikimi yok .public static void main(String[] args) throws Exception {
if (args.length == 0)
return;
else
args = Arrays.copyOf(args, args.length - 1);
List<String> command = new ArrayList<>(32);
appendJavaExecutable(command);
appendVMArgs(command);
appendClassPath(command);
appendEntryPoint(command);
appendArgs(command, args);
System.out.println(command);
try {
new ProcessBuilder(command).inheritIO().start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private static void appendJavaExecutable(List<String> cmd) {
cmd.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
}
private static void appendVMArgs(Collection<String> cmd) {
Collection<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
String javaToolOptions = System.getenv("JAVA_TOOL_OPTIONS");
if (javaToolOptions != null) {
Collection<String> javaToolOptionsList = Arrays.asList(javaToolOptions.split(" "));
vmArguments = new ArrayList<>(vmArguments);
vmArguments.removeAll(javaToolOptionsList);
}
cmd.addAll(vmArguments);
}
private static void appendClassPath(List<String> cmd) {
cmd.add("-cp");
cmd.add(ManagementFactory.getRuntimeMXBean().getClassPath());
}
private static void appendEntryPoint(List<String> cmd) {
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
StackTraceElement stackTraceElement = stackTrace[stackTrace.length - 1];
String fullyQualifiedClass = stackTraceElement.getClassName();
String entryMethod = stackTraceElement.getMethodName();
if (!entryMethod.equals("main"))
throw new AssertionError("Entry point is not a 'main()': " + fullyQualifiedClass + '.' + entryMethod);
cmd.add(fullyQualifiedClass);
}
private static void appendArgs(List<String> cmd, String[] args) {
cmd.addAll(Arrays.asList(args));
}
V1.1 Hata düzeltme: JAVA_TOOL_OPTIONS ayarlanmamışsa boş işaretçi
Misal:
$ java -cp Temp.jar Temp a b c d e
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c, d]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp]
$
System.err.println("Someone is Restarting me...");
setVisible(false);
try {
Thread.sleep(600);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
setVisible(true);
Sanırım uygulamayı gerçekten durdurmak istemiyorsunuz, ancak "yeniden başlatmak" istiyorsunuz. Bunun için bunu kullanabilir ve uykudan önce ve görünmez pencereden sonra "Sıfırla" yı ekleyebilirsiniz.