Пишу десктопный хэлловорлд на JavaFX, который умеет сосать обновления с сервака и обновлять сам себя до текущей стабильной версии.
Реализовал это так:
1. Основное приложение (App.jar) проверяет, есть ли новые версии, если есть, качает новую версию себя (NewVerApp.jar) в свою корневую папку и запускает консольное приложение Uppdater.jar, которому передает свой PID. (Предполагается что запущена только одна версия приложения)
2. Uppdater.jar закрывает App.jar по его PID и пытается удалить App.jar
3. Uppdater.jar переименовывает NewVerApp.jar в App.jar
4. Uppdater.jar запускает App.jar
5. Uppdater.jar закрывается, обновление произведено успешно.
Скользкий момент в том, что когда Uppdater.jar закрывает App.jar, создается подпроцесс, который выполняется некоторое время:
/**
* Закрывает приложение по его PID
* @param pid
*/
public void killAppByPID(String pid){
Runtime r = Runtime.getRuntime();
try {
Process p = r.exec("taskkill /F /PID " + pid);
} catch (IOException e) {
e.printStackTrace();
}
}
Пока файл закрывается - он заблокирован (его нельзя удалить), и время блокировки больше чем
p.isAlive()
Поэтому я решил удалять так:
import java.io.File;
public class RenameFileThread implements Runnable{
private File srcFileNew, srcFileOld;
private Thread go;
// File srcFileNew = new File("NewVerApp.jar");
// File srcFileOld = new File("App.jar");
RenameFileThread(File srcFileNew, File srcFileOld){
this.srcFileNew = srcFileNew;
this.srcFileOld = srcFileOld;
go = new Thread(this);
go.start();
}
public void run(){
Thread th = Thread.currentThread();
while(go == th){
// если старый файл успешно переименован, называем новый именем старого файла
if (srcFileOld.delete()){
srcFileNew.renameTo(new File("App.jar"));
this.stop();
}
}
System.out.println("End of thread.");
}
public void stop(){ go = null; }
}
Файл удаляется в потоке, когда он успешно удален - новый файл переименовывается.
После этого производится запуск приложения:
Runtime r = Runtime.getRuntime();
try {
Process p = r.exec("java -jar App.jar");
} catch (IOException e) {
e.printStackTrace();
}
Вопрос
Есть ли ошибки в этом подходе?
Как грамотно организовать такой процесс?
Какие еще могут быть решения?