런타임에 스스로 업데이트 할 수있는 Java 애플리케이션을 어떻게 작성할 수 있습니까?


91

주어진 URL에서 새 버전 (.jar 파일)을 다운로드 한 다음 런타임에 업데이트 할 수있는 Java 응용 프로그램 (서버 응용 프로그램)을 구현하고 싶습니다.

이를 수행하는 가장 좋은 방법은 무엇이며 가능합니까?

응용 프로그램이 새 .jar 파일을 다운로드하여 시작할 수 있다고 생각합니다. 그러나 어떻게 핸드 오버를해야합니까? 예를 들어 새 응용 프로그램이 언제 시작되고 종료되는지 알 수 있습니다. 아니면 더 좋은 방법이 있습니까?


4
Java Web Start를 보셨습니까? 하지만 런타임에 자체적으로 업데이트되지는 않습니다 (다시 시작해야 함). OSGi를 살펴볼 필요가 있기 때문입니다.
Thilo

@Thilo : 주어진 URL에서 파일을 다운로드 한 다음 실행중인 jar 파일에서 Linux 명령으로 시작하는 것이 쉬울 것이라고 생각합니다.
Jonas

1
Java WebStart API의 설계로 인해 실행 중 업데이트가 불가능합니다. 운수 나쁘게.
Thorbjørn Ravn Andersen


당신이 바위 @meain 보스, 당신은 내 일을 :)했다
칼리드 iltaf

답변:


68

솔루션의 기본 구조는 다음과 같습니다.

  • 앱의 최신 버전을 반복적으로로드하고 (필요한 경우) 실행하는 메인 루프가 있습니다.

  • 응용 프로그램은 그 일을하지만 주기적으로 다운로드 URL을 확인합니다. 새 버전을 감지하면 런처로 돌아갑니다.

이를 구현할 수있는 방법에는 여러 가지가 있습니다. 예를 들면 :

  • 런처는 대체되는 JAR 파일에서 애플리케이션을 실행하기 위해 새 JVM을 시작하는 랩퍼 스크립트 또는 바이너리 애플리케이션 일 수 있습니다.

  • 런처는 새 JAR에 대한 클래스 로더를 작성하고 진입 점 클래스를로드하고 여기에 일부 메소드를 호출하는 Java 애플리케이션 일 수 있습니다. 이런 식으로하면 클래스 로더 저장소 누수를 감시해야하지만 어렵지 않습니다. (재실행 후 JAR에서로드 된 클래스가있는 객체에 도달 할 수 없는지 확인하기 만하면됩니다.)

외부 래퍼 접근 방식의 장점은 다음과 같습니다.

  • JAR 하나만 필요합니다.
  • 전체 Java 앱을 대체 할 수 있습니다.
  • 앱 등에서 생성 된 보조 스레드는 특별한 종료 로직없이 사라집니다.
  • 응용 프로그램 충돌 등으로부터 복구를 처리 할 수도 있습니다.

두 번째 접근 방식에는 두 개의 JAR이 필요하지만 다음과 같은 이점이 있습니다.

  • 솔루션은 순수 Java이며 이식 가능합니다.
  • 전환이 더 빨라지고
  • 다시 시작하는 동안 상태를 더 쉽게 유지할 수 있습니다 (모듈로 누출 문제).

"최상의"방법은 특정 요구 사항에 따라 다릅니다.

또한 다음 사항에 유의해야합니다.

  • 자동 업데이트에는 보안 위험이 있습니다. 일반적으로 업데이트를 제공하는 서버가 손상되었거나 업데이트를 제공하는 메커니즘이 공격에 취약한 경우 자동 업데이트로 인해 클라이언트가 손상 될 수 있습니다.

  • 고객에게 피해를주는 업데이트를 고객에게 푸시하면 법적 위험이있을 수 있으며 비즈니스 평판에 위험이 발생할 수 있습니다.


바퀴를 재발 명하지 않는 방법을 찾을 수 있다면 좋을 것입니다. 제안 사항은 다른 답변을 참조하십시오.


43

저는 현재 JAVA Linux Daemon을 개발 중이며 자동 업데이트 메커니즘을 구현해야했습니다. 내 응용 프로그램을 하나의 jar 파일로 제한하고 간단한 해결책을 찾았습니다.

업데이트 자체에 업데이트 프로그램 응용 프로그램을 압축합니다.

응용 프로그램 : 응용 프로그램이 최신 버전을 감지하면 다음을 수행합니다.

  1. 업데이트 다운로드 (Zipfile)
  2. 응용 프로그램 및 ApplicationUpdater 추출 (모두 zip 파일에 있음)
  3. 업데이터 실행

ApplicationUpdater : 업데이트 프로그램이 실행되면 다음을 수행합니다.

  1. 응용 프로그램을 중지하십시오 (제 경우에는 init.d를 통한 데몬)
  2. 다운로드 한 jar 파일을 복사하여 현재 애플리케이션을 덮어 씁니다.
  3. 응용 프로그램 시작
  4. 대청소.

누군가에게 도움이되기를 바랍니다.


12

이것은 알려진 문제이며 바퀴를 재발 명하지 않는 것이 좋습니다. 자신의 해킹을 작성하지 말고 다른 사람들이 이미 한 것을 사용하십시오.

고려해야 할 두 가지 상황 :

  1. 앱은 자체 업데이트가 가능하고 업데이트 중에도 계속 실행되어야합니다 (서버 앱, 임베디드 앱). OSGi로 이동 : 번들 또는 Equinox p2 .

  2. 앱은 데스크톱 앱이며 설치 프로그램이 있습니다. 업데이트 옵션이있는 설치 프로그램이 많이 있습니다. 설치자 목록을 확인하십시오 .


12

최근 Java 9의 모듈 시스템과 완벽하게 호환되는 update4j 를 만들었습니다 .

다시 시작하지 않고도 새 버전을 원활하게 시작합니다.


부트 스트랩 / 비즈니스 애플리케이션 패러다임을 사용합니다. 부트 스트랩은 비즈니스 애플리케이션을로드 및 언로드하며 일반적으로 업데이트를 수행하는 것이 부트 스트랩입니다.
Mordechai

10

jEdit의 유사한 메커니즘에서 영감을 받아 런타임에 플러그인을로드하고 즉시 사용할 수있는 Java 애플리케이션을 작성했습니다. jEdit는 오픈 소스이므로 작동 방식을 볼 수있는 옵션이 있습니다.

이 솔루션은 사용자 정의 ClassLoader를 사용하여 jar에서 파일을로드합니다. 일단로드되면 새 jar에서 해당 main메서드 로 작동 할 메서드를 호출 할 수 있습니다 . 그런 다음 까다로운 부분은 가비지 수집이 가능하도록 이전 코드에 대한 모든 참조를 제거하는 것입니다. 나는 그 부분에 대한 전문가가 아니고 그것을 작동하도록 만들었지 만 쉽지 않았습니다.


Java에 대해서는 모르지만 C #에서는 AppDomains를 사용하여 코드를 명시 적으로 언로드 할 수 있습니다. Java에도 비슷한 개념이있을 수 있습니다.
Merlyn Morgan-Graham

1
고마워요, 이것이 갈 길인 것 같습니다. ClassLoaderNetworkClassLoader 에 대한 JavaDoc에 대한 예제가 있습니다
Jonas

동일한 JVM 내에서 정적 변수에 문제가 발생 했습니까? 아니면 영구 생성은 어떻습니까? 나는 이것이 수업 하역과 관련하여 문제를 일으킬 수 있다고 들었습니다.
ide

5
  1. 첫 번째 방법 : 바람둥이를 사용하고 시설을 배포합니다.
  2. 두 번째 방법 : 응용 프로그램을 두 부분 (기능 및 업데이트)으로 분할하고 업데이트 부분이 기능 부분을 대체하도록합니다.
  3. 세 번째 방법 : 서버 응용 프로그램에서 새 버전을 다운로드 한 다음 이전 버전이 바인딩 된 포트를 릴리스 한 다음 이전 버전이 새 버전을 실행 (프로세스 시작) 한 다음 이전 버전이 응용 프로그램 포트에 대한 요청을 새 버전으로 전송하여 이전 버전, 이전 버전을 삭제합니다. 종료되고 새 버전은 이전 버전을 삭제합니다. 이렇게 : 대체 텍스트

두 번째 방법은 흥미로운 디자인 인 것 같습니다.
Jonas

2

이것이 반드시 최고 는 아닙니다. 방법은 아니지만 귀하에게 적합 할 수 있습니다.

부트 스트랩 애플리케이션을 작성할 수 있습니다 (WoW를 플레이 한 경우 월드 오브 워크래프트 런처라고도 함). 해당 부트 스트랩은 업데이트 확인을 담당합니다.

  • 사용 가능한 업데이트가 있으면 사용자에게 제공하고 다운로드, 설치 등을 처리합니다.
  • 응용 프로그램이 최신 상태이면 사용자가 응용 프로그램을 시작할 수 있습니다.
  • 선택적으로 최신 상태가 아니더라도 사용자가 애플리케이션을 실행하도록 허용 할 수 있습니다.

이렇게하면 애플리케이션을 강제 종료하는 것에 대해 걱정할 필요가 없습니다.

애플리케이션이 웹 기반이고 최신 클라이언트가 있어야하는 경우 애플리케이션이 실행되는 동안 버전 확인을 수행 할 수도 있습니다. 서버와의 정상적인 통신 (일부 또는 모든 호출) 또는 둘 다를 수행하면서 간격을두고 수행 할 수 있습니다.

최근에 작업 한 제품의 경우 시작시 (부트 스트 래퍼 앱없이 기본 창이 표시되기 전) 및 서버 호출 중에 버전 확인을 수행했습니다. 클라이언트가 오래되었을 때 우리는 사용자가 수동으로 종료하도록 의존했지만 서버에 대한 어떠한 조치도 금지했습니다.

메인 창을 열기 전에 Java가 UI 코드를 호출 할 수 있는지 알 수 없습니다. 우리는 C # / WPF를 사용하고있었습니다.


고마워요, 그렇게하는 좋은 방법입니다. 그러나 그것은 서버 응용 프로그램이므로 어떤 조치를 취할 수있는 사용자가 없습니다. 그리고 저는 단지 하나의 jar 파일을 선호하므로 사용자 easyli는 단일 jar를 다운로드하여 처음부터 시작할 수 있지만 그 후에는 사용자 상호 작용이 필요하지 않습니다.
Jonas

"서버 애플리케이션"이라고하면 로그인 한 상태에서 서버에서 직접 실행되는 애플리케이션이라는 의미입니까?
Merlyn Morgan-Graham

@Jonas : .jar 파일이 어떻게 작동하는지 잘 이해하지 못해서 거기에서 많은 서비스를받을 수 없습니다. Java에 특정한 답변을 선호하는지 이해할 수 있습니다. 희망이 :) 적어도 생각 당신에게 음식을 제공합니다
Merlyn 모건 - 그레이엄

@Merlyn : 아이디어를 공유해 주셔서 감사합니다. 예, Linux 서버에서 실행되는 Java 애플리케이션이며 해당 서버에 로그인 한 사람이 없습니다.
Jonas

1
@Jonas : 당신이 이미 이것에 대해 생각하지 않았다는 것은 아니지만-내가 본 대부분의 웹 서비스에는 수동 배포 전략이 있습니다. 업데이트를 확인하는 방법에 대해주의해야 할 수 있습니다. 버그가 있거나 손상된 코드를 푸시하거나 다중 파일 배포로 일부만 완료하면 서버가 자체 업데이트를 시도 할 수 있으며 서버가 손상 될 수 있습니다.
Merlyn Morgan-Graham

2

Equinox 플러그인을 사용하여 애플리케이션을 빌드 하는 경우 P2 프로비저닝 시스템 을 사용 하여이 문제에 대한 기성 솔루션을 얻을 수 있습니다. 업데이트 후 서버 자체를 다시 시작해야합니다.


1

예를 들어 중간 공격과 같은 새 jar (등)를 다운로드 할 때 보안 문제가 있습니다. 항상 다운로드 가능한 업데이트에 서명해야합니다.

JAX2015에서 Adam Bien은 바이너리 업데이트를 위해 JGit을 사용하는 것에 대해 말했습니다. 슬프게도 튜토리얼을 찾을 수 없습니다.

독일어 소스.

아담 비엔는 업데이터가 만든 여기 참조

여기 에 javaFX 프론트 엔드로 포크했습니다 . 자동 서명 작업도하고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.