Jak napisać aplikację Java, która może aktualizować się w czasie wykonywania?

91

Chciałbym zaimplementować aplikację java (aplikację serwerową), która może pobrać nową wersję (plik .jar) z podanego adresu URL, a następnie zaktualizować się w czasie wykonywania.

Jaki jest najlepszy sposób na zrobienie tego i czy jest to możliwe?

Domyślam się, że aplikacja może pobrać nowy plik .jar i go uruchomić. Ale jak mam wykonać przekazanie, np. Wiedzieć, kiedy nowa aplikacja jest uruchomiona, a następnie wyjść. Czy jest na to lepszy sposób?

Jonas
źródło
4
Czy spojrzałeś na Java Web Start? Uważam jednak, że nie aktualizuje się on sam w czasie wykonywania (wymagany restart), w tym celu prawdopodobnie musisz spojrzeć na OSGi.
Thilo
@Thilo: Myślę, że łatwo byłoby pobrać plik z podanego adresu URL, a następnie uruchomić go poleceniem linux z uruchomionego pliku jar.
Jonas
1
Konstrukcja Java WebStart API uniemożliwia aktualizację podczas pracy. Niestety.
Thorbjørn Ravn Andersen
@meain boss you rock, you made my day :)
iltaf khalid

Odpowiedzi:

68

Podstawowa struktura rozwiązania jest następująca:

  • Istnieje główna pętla odpowiedzialna za wielokrotne ładowanie najnowszej wersji aplikacji (jeśli jest wymagana) i jej uruchamianie.

  • Aplikacja robi swoje, ale okresowo sprawdza adres URL pobierania. Jeśli wykryje nową wersję, wraca do programu uruchamiającego.

Można to wdrożyć na wiele sposobów. Na przykład:

  • Program uruchamiający może być skryptem opakowującym lub aplikacją binarną, która uruchamia nową maszynę JVM w celu uruchomienia aplikacji z zastępowanego pliku JAR.

  • Program uruchamiający może być aplikacją Java, która tworzy moduł ładujący klasy dla nowego pliku JAR, ładuje klasę punktu wejścia i wywołuje na niej jakąś metodę. Jeśli zrobisz to w ten sposób, musisz uważać na wycieki pamięci klasy ładującej, ale to nie jest trudne. (Musisz tylko upewnić się, że żadne obiekty z klasami załadowanymi z pliku JAR nie są osiągalne po ponownym uruchomieniu).

Zalety podejścia zewnętrznego opakowania to:

  • potrzebujesz tylko jednego JARa,
  • możesz zamienić całą aplikację Java,
  • wszystkie wątki pomocnicze utworzone przez aplikację itp. zostaną usunięte bez specjalnej logiki zamykania, a
  • możesz również poradzić sobie z odzyskiwaniem po awariach aplikacji itp.

Drugie podejście wymaga dwóch plików JAR, ale ma następujące zalety:

  • rozwiązanie jest w czystej Javie i przenośne,
  • przezbrojenie będzie szybsze i
  • możesz łatwiej zachować stan po ponownym uruchomieniu (problemy z wyciekiem modulo).

„Najlepszy” sposób zależy od konkretnych wymagań.

Należy również zauważyć, że:

  • Automatyczne aktualizowanie wiąże się z zagrożeniami bezpieczeństwa. Ogólnie rzecz biorąc, jeśli serwer udostępniający aktualizacje jest zagrożony lub mechanizmy zapewniające aktualizacje są podatne na atak, automatyczne aktualizowanie może doprowadzić do naruszenia bezpieczeństwa klienta (ów).

  • Rozesłanie aktualizacji do klienta, która wyrządzi klientowi szkodę, może wiązać się z ryzykiem prawnym oraz zagrozić reputacji Twojej firmy.


Byłoby dobrze, gdybyś mógł znaleźć sposób, aby uniknąć ponownego wynalezienia koła. Zobacz inne odpowiedzi, aby uzyskać sugestie.

Stephen C.
źródło
43

Obecnie pracuję nad demonem JAVA Linux i miałem też potrzebę zaimplementowania mechanizmu automatycznej aktualizacji. Chciałem ograniczyć swoją aplikację do jednego pliku jar i znalazłem proste rozwiązanie:

Spakuj aplikację aktualizującą do samej aktualizacji.

Aplikacja : gdy aplikacja wykryje nowszą wersję, wykonuje następujące czynności:

  1. Pobierz aktualizację (Zipfile)
  2. Wyodrębnij aplikację i ApplicationUpdater (wszystko w pliku zip)
  3. Uruchom aktualizator

ApplicationUpdater : gdy aktualizator jest uruchomiony, wykonuje następujące czynności:

  1. Zatrzymaj aplikację (w moim przypadku demona przez init.d)
  2. Skopiuj pobrany plik jar, aby nadpisać bieżącą aplikację
  3. Uruchom aplikację
  4. Sprzątać.

Mam nadzieję, że to komuś pomoże.

Berdus
źródło
12

To znany problem i odradzam wymyślanie na nowo koła - nie pisz własnego hacka, po prostu użyj tego, co zrobili już inni.

Dwie sytuacje, które musisz wziąć pod uwagę:

  1. Aplikacja musi mieć możliwość samodzielnej aktualizacji i działać nawet podczas aktualizacji (aplikacja serwerowa, aplikacje wbudowane). Idź z OSGi: Bundles lub Equinox p2 .

  2. Aplikacja to aplikacja komputerowa z instalatorem. Istnieje wiele instalatorów z opcją aktualizacji. Sprawdź listę instalatorów .

Peter Knego
źródło
12

Niedawno stworzyłem update4j, który jest w pełni kompatybilny z systemem modułów Java 9.

Nowa wersja bezproblemowo uruchomi się bez restartu.

Mordechai
źródło
Korzystając z paradygmatu bootstrap / aplikacji biznesowych. Bootstrap ładuje i zwalnia aplikację biznesową i to bootstrap zazwyczaj wykonuje aktualizację.
Mordechai
10

Napisałem aplikację Java, która może ładować wtyczki w czasie wykonywania i natychmiast zacząć ich używać, zainspirowana podobnym mechanizmem w jEdit. jEdit jest open source, więc możesz sprawdzić, jak to działa.

Rozwiązanie wykorzystuje niestandardowy ClassLoader do ładowania plików z jar. Po załadowaniu możesz wywołać jakąś metodę z nowego jar, która będzie działać jako mainmetoda. Następnie trudna część polega na tym, aby pozbyć się wszystkich odniesień do starego kodu, aby można go było usunąć jako śmieci. Nie jestem ekspertem w tej kwestii, udało mi się, ale nie było to łatwe.

Brad Mace
źródło
Nie wiem o Javie, ale w C # możesz użyć AppDomains do jawnego zwolnienia kodu. Być może w Javie istnieje podobna koncepcja.
Merlyn Morgan-Graham
1
Dzięki, wydaje się, że to właściwy sposób. Jest przykład a NetworkClassLoaderw JavaDoc dla ClassLoader
Jonas,
Czy napotkałeś problemy ze zmiennymi statycznymi w tej samej JVM, czy co z trwałym generowaniem? Słyszałem, że mogą one powodować problemy związane z rozładowywaniem klas.
ide
5
  1. Pierwszy sposób: użyj tomcat i jego urządzeń do wdrażania.
  2. Drugi sposób: podzielić aplikację na dwie części (funkcjonalną i aktualizacyjną) i pozwolić aktualizować część zastępującą funkcję.
  3. Trzeci sposób: w aplikacji serwera wystarczy pobrać nową wersję, następnie stara wersja zwalnia powiązany port, następnie stara wersja uruchamia nową wersję (rozpoczyna proces), a następnie stara wersja wysyła żądanie na porcie aplikacji do nowej wersji, aby usunąć starą wersję, starą wersję kończy działanie, a nowa wersja usuwa starą wersję. Lubię to: tekst alternatywny
jnr
źródło
Twój drugi sposób wydaje się być interesującym projektem.
Jonas,
2

Niekoniecznie jest to najlepszy sposób, ale może zadziałać dla Ciebie.

Możesz napisać aplikację ładującą (ala program uruchamiający World of Warcraft, jeśli grałeś w WoW). Ten bootstrap jest odpowiedzialny za sprawdzanie aktualizacji.

  • Jeśli aktualizacja jest dostępna, zaoferuje ją użytkownikowi, zajmie się pobieraniem, instalacją itp.
  • Jeśli aplikacja jest aktualna, umożliwi to użytkownikowi uruchomienie aplikacji
  • Opcjonalnie możesz pozwolić użytkownikowi na uruchomienie aplikacji, nawet jeśli nie jest aktualna

Dzięki temu nie musisz się martwić wymuszeniem zamknięcia aplikacji.

Jeśli Twoja aplikacja jest oparta na sieci Web i ważne jest, aby miała aktualnego klienta, możesz również sprawdzać wersję podczas działania aplikacji. Możesz to robić w odstępach czasu, podczas normalnej komunikacji z serwerem (niektóre lub wszystkie połączenia) lub jedno i drugie.

W przypadku produktu, nad którym ostatnio pracowałem, sprawdzaliśmy wersję po uruchomieniu (bez aplikacji do bootowania, ale przed pojawieniem się okna głównego) i podczas połączeń z serwerem. Kiedy klient był nieaktualny, polegaliśmy na tym, że użytkownik zakończył pracę ręcznie, ale zabroniliśmy jakichkolwiek działań przeciwko serwerowi.

Pamiętaj, że nie wiem, czy Java może wywołać kod interfejsu użytkownika, zanim otworzysz główne okno. Używaliśmy C # / WPF.

Merlyn Morgan-Graham
źródło
Dzięki, to miły sposób na zrobienie tego. Ale jest to aplikacja serwerowa, więc nie ma użytkownika, który może wykonać jakąś akcję. I wolałbym, żeby był to tylko jeden plik jar, więc użytkownik easyli może pobrać pojedynczy plik jar i uruchomić go od początku, ale potem nie chciałbym mieć żadnych interakcji z użytkownikiem.
Jonas
Kiedy mówisz „aplikacja serwerowa”, czy masz na myśli, że jest to aplikacja uruchamiana bezpośrednio na serwerze po zalogowaniu?
Merlyn Morgan-Graham
@Jonas: Nie rozumiem zbytnio, jak działają pliki .jar, więc nie mogę tam być zbyt pomocny. Rozumiem, jeśli wolisz odpowiedź dotyczącą języka Java. Miejmy nadzieję, że to przynajmniej daje do myślenia :)
Merlyn Morgan-Graham
@Merlyn: Jestem wdzięczny za podzielenie się swoimi pomysłami. Tak, jest to aplikacja Java, która będzie działać na serwerze Linux i nikt nie jest zalogowany na tym serwerze.
Jonas
1
@Jonas: Nie żebyś o tym wcześniej nie pomyślał, ale - Większość usług internetowych, które widziałem, ma strategię ręcznego wdrażania. Możesz zachować ostrożność podczas sprawdzania dostępności aktualizacji. Jeśli opublikujesz błędny / uszkodzony kod lub wykonasz tylko częściowo za pomocą wdrożenia wielu plików, serwer może spróbować się zaktualizować, a wtedy masz uszkodzony serwer.
Merlyn Morgan-Graham
2

Jeśli tworzysz swoją aplikację przy użyciu wtyczek Equinox , możesz skorzystać z systemu P2 Provisioning, aby uzyskać gotowe rozwiązanie tego problemu. Będzie to wymagało ponownego uruchomienia serwera po aktualizacji.

Tom Crockett
źródło
1

Widzę problem bezpieczeństwa podczas ściągania nowego jar (itp.), Np. Atak man in the middle. Zawsze musisz podpisać aktualizację do pobrania.

Na JAX2015 Adam Bien opowiedział o używaniu JGit do aktualizacji binariów. Niestety nie mogłem znaleźć żadnych tutoriali.

Źródło w języku niemieckim.

Adam Bien stworzył aktualizator, patrz tutaj

Tutaj rozwidliłem to z jakimś interfejsem javaFX. Pracuję również nad automatycznym podpisem.

Kaito
źródło