Bagaimana cara menulis aplikasi Java yang dapat memperbarui dirinya sendiri saat runtime?

91

Saya ingin mengimplementasikan aplikasi java (aplikasi server) yang dapat mendownload versi baru (file .jar) dari url tertentu, dan kemudian mengupdate dirinya sendiri saat runtime.

Apa cara terbaik untuk melakukan ini dan apakah mungkin?

Saya kira aplikasi tersebut dapat mengunduh file .jar baru dan memulainya. Tetapi bagaimana saya harus melakukan serah terima, misalnya mengetahui kapan aplikasi baru dimulai dan kemudian keluar. Atau apakah ada cara yang lebih baik untuk melakukan ini?

Jonas
sumber
4
Pernahkah Anda melihat Java Web Start? Itu tidak memperbarui dirinya sendiri pada saat runtime, saya yakin, (diperlukan restart), untuk itu Anda mungkin perlu melihat OSGi.
Thilo
@Thilo: Saya pikir akan mudah untuk mendownload file dari url tertentu, dan kemudian memulainya dengan perintah linux dari file jar yang sedang berjalan.
Jonas
1
Desain Java WebStart API membuatnya tidak mungkin diperbarui saat dijalankan. Sayangnya.
Thorbjørn Ravn Andersen
@ bos utama Anda keren, Anda membuat hari saya :)
iltaf khalid

Jawaban:

68

Struktur dasar solusi adalah sebagai berikut:

  • Ada loop utama yang bertanggung jawab untuk berulang kali memuat versi terbaru aplikasi (jika diperlukan) dan meluncurkannya.

  • Aplikasi melakukan tugasnya, tetapi secara berkala memeriksa URL unduhan. Jika mendeteksi versi baru, itu keluar kembali ke peluncur.

Ada beberapa cara untuk menerapkan ini. Sebagai contoh:

  • Peluncur bisa berupa skrip pembungkus atau aplikasi biner yang memulai JVM baru untuk menjalankan aplikasi dari file JAR yang diganti.

  • Peluncur dapat berupa aplikasi Java yang membuat classloader untuk JAR baru, memuat kelas entrypoint dan memanggil beberapa metode di atasnya. Jika Anda melakukannya dengan cara ini, Anda harus memperhatikan kebocoran penyimpanan classloader, tetapi itu tidak sulit. (Anda hanya perlu memastikan bahwa tidak ada objek dengan kelas yang dimuat dari JAR yang dapat dijangkau setelah Anda meluncurkan ulang.)

Keuntungan dari pendekatan pembungkus eksternal adalah:

  • Anda hanya membutuhkan satu JAR,
  • Anda dapat mengganti seluruh aplikasi Java,
  • utas sekunder apa pun yang dibuat oleh aplikasi, dll. akan hilang tanpa logika shutdown khusus, dan
  • Anda juga dapat menangani pemulihan dari kerusakan aplikasi, dll.

Pendekatan kedua membutuhkan dua JAR, tetapi memiliki keuntungan sebagai berikut:

  • solusinya adalah Java murni dan portabel,
  • pergantian akan lebih cepat, dan
  • Anda dapat lebih mudah mempertahankan status saat memulai ulang (masalah kebocoran modulo).

Cara "terbaik" tergantung pada kebutuhan spesifik Anda.

Perlu juga diperhatikan bahwa:

  • Ada risiko keamanan dengan pembaruan otomatis. Secara umum, jika server yang menyediakan pembaruan terganggu, atau jika mekanisme untuk menyediakan pembaruan rentan terhadap serangan, maka pembaruan otomatis dapat menyebabkan gangguan pada klien.

  • Mendorong pembaruan ke klien yang menyebabkan kerusakan pada klien dapat memiliki risiko hukum, dan risiko reputasi bisnis Anda.


Jika Anda dapat menemukan cara untuk menghindari penemuan kembali roda, itu bagus. Lihat jawaban lain untuk saran.

Stephen C
sumber
43

Saat ini saya sedang mengembangkan JAVA Linux Daemon dan juga perlu menerapkan mekanisme pembaruan otomatis. Saya ingin membatasi aplikasi saya ke satu file jar, dan menghasilkan solusi sederhana:

Kemas aplikasi pembaru dalam pembaruan itu sendiri.

Aplikasi : Saat aplikasi mendeteksi versi yang lebih baru, ia melakukan hal berikut:

  1. Unduh pembaruan (Zipfile)
  2. Ekstrak Application dan ApplicationUpdater (semua di zipfile)
  3. Jalankan updater

ApplicationUpdater : Saat pembaru menjalankannya, ia melakukan hal berikut:

  1. Hentikan Aplikasi (dalam kasus saya, daemon melalui init.d)
  2. Salin file jar yang diunduh untuk menimpa Aplikasi saat ini
  3. Mulai Aplikasi
  4. Membersihkan.

Semoga bisa membantu seseorang.

Berdus
sumber
12

Ini adalah masalah yang diketahui dan saya sarankan untuk tidak menciptakan kembali roda - jangan menulis retasan Anda sendiri, cukup gunakan apa yang telah dilakukan orang lain.

Dua situasi yang perlu Anda pertimbangkan:

  1. Aplikasi harus dapat diperbarui sendiri dan terus berjalan bahkan selama pembaruan (aplikasi server, aplikasi tersemat). Gunakan OSGi: Bundles atau Equinox p2 .

  2. Aplikasi adalah aplikasi desktop dan memiliki penginstal. Ada banyak penginstal dengan opsi pembaruan. Periksa daftar pemasang .

Peter Knego
sumber
12

Saya baru saja membuat update4j yang sepenuhnya kompatibel dengan sistem modul Java 9.

Ini akan memulai versi baru dengan mulus tanpa restart.

Mordechai
sumber
Dengan menggunakan paradigma bootstrap / aplikasi bisnis. Bootstrap memuat dan membongkar aplikasi bisnis dan bootstrap-lah yang biasanya melakukan pembaruan.
Mordechai
10

Saya telah menulis aplikasi Java yang dapat memuat plugin saat runtime dan segera mulai menggunakannya, terinspirasi oleh mekanisme serupa di jEdit. jEdit adalah open source sehingga Anda memiliki opsi untuk melihat cara kerjanya.

Solusinya menggunakan ClassLoader kustom untuk memuat file dari jar. Setelah dimuat, Anda dapat menjalankan beberapa metode dari toples baru yang akan bertindak sebagai mainmetodenya. Kemudian bagian yang sulit adalah memastikan Anda menyingkirkan semua referensi ke kode lama sehingga bisa dikumpulkan sampahnya. Saya bukan ahli dalam hal itu, saya telah membuatnya berhasil tetapi itu tidak mudah.

Brad Mace
sumber
Saya tidak tahu tentang Java, tetapi di C #, Anda dapat menggunakan AppDomains untuk membongkar kode secara eksplisit. Mungkin ada konsep serupa di Jawa.
Merlyn Morgan-Graham
1
Terima kasih, sepertinya ini cara yang harus dilakukan. Ada contoh a NetworkClassLoaderdi JavaDoc
Jonas
Apakah Anda mengalami masalah dengan variabel statis dalam JVM yang sama, atau bagaimana dengan generasi permanen? Saya telah mendengar ini dapat menghadirkan masalah sehubungan dengan pembongkaran kelas.
ide
5
  1. Cara pertama: gunakan tomcat dan fasilitas penyebarannya.
  2. Cara kedua: untuk membagi aplikasi menjadi dua bagian (fungsional dan pembaruan) dan membiarkan bagian pembaruan menggantikan bagian fungsi.
  3. Cara ketiga: Dalam aplikasi server Anda cukup unduh versi baru, kemudian versi lama melepaskan port terikat, kemudian versi lama menjalankan versi baru (memulai proses), kemudian versi lama mengirimkan permintaan pada port aplikasi ke versi baru untuk menghapus versi lama, versi lama berakhir dan versi baru menghapus versi lama. Seperti ini: teks alt
jnr
sumber
Cara kedua Anda tampaknya merupakan desain yang menarik.
Jonas
2

Ini belum tentu yang terbaik cara , tetapi mungkin berhasil untuk Anda.

Anda dapat menulis aplikasi bootstrap (ala peluncur World of Warcraft, jika Anda telah memainkan WoW). Bootstrap itu bertanggung jawab untuk memeriksa pembaruan.

  • Jika pembaruan tersedia, itu akan menawarkannya kepada pengguna, menangani unduhan, instalasi, dll.
  • Jika aplikasinya mutakhir, itu akan memungkinkan pengguna untuk meluncurkan aplikasi
  • Secara opsional, Anda dapat mengizinkan pengguna untuk meluncurkan aplikasi, meskipun tidak mutakhir

Dengan cara ini Anda tidak perlu khawatir tentang memaksa keluar dari aplikasi Anda.

Jika aplikasi Anda berbasis web, dan jika penting bahwa mereka memiliki klien terbaru, maka Anda juga dapat melakukan pemeriksaan versi saat aplikasi berjalan. Anda dapat melakukannya dalam interval, saat melakukan komunikasi normal dengan server (beberapa atau semua panggilan), atau keduanya.

Untuk produk yang baru-baru ini saya kerjakan, kami melakukan pemeriksaan versi saat peluncuran (tanpa aplikasi boot strapper, tetapi sebelum jendela utama muncul), dan selama panggilan ke server. Jika klien sudah tidak berlaku lagi, kami mengandalkan pengguna untuk keluar secara manual, tetapi melarang tindakan apa pun terhadap server.

Harap perhatikan bahwa saya tidak tahu apakah Java dapat memanggil kode UI sebelum Anda membuka jendela utama. Kami menggunakan C # / WPF.

Merlyn Morgan-Graham
sumber
Terima kasih, itu cara yang bagus untuk melakukannya. Tetapi ini adalah aplikasi server sehingga tidak ada pengguna yang dapat mengambil tindakan. Dan saya lebih suka bahwa itu hanya satu file jar, jadi pengguna easyli dapat mengunduh satu jar dan memulainya dari awal, tetapi setelah itu saya tidak ingin ada interaksi pengguna.
Jonas
Saat Anda mengatakan "aplikasi server", apakah yang Anda maksud itu adalah aplikasi yang dijalankan langsung di server, saat masuk?
Merlyn Morgan-Graham
@ Jonas: Saya tidak mengerti banyak tentang cara kerja file .jar, jadi saya tidak bisa banyak melayani di sana. Saya bisa mengerti jika Anda lebih suka jawaban khusus untuk Java. Mudah-mudahan ini memberi Anda makanan untuk dipikirkan, setidaknya :)
Merlyn Morgan-Graham
@Merlyn: Saya bersyukur telah membagikan ide-ide Anda. Ya, ini adalah aplikasi Java yang akan berjalan di server Linux, dan tidak ada yang masuk ke server itu.
Jonas
1
@ Jonas: Bukan karena Anda belum memikirkan hal ini, tetapi - Sebagian besar layanan web yang saya lihat memiliki strategi penerapan manual. Anda mungkin ingin berhati-hati tentang cara Anda memeriksa pembaruan. Jika Anda mendorong kode buggy / rusak, atau hanya menyelesaikan sebagian dengan penerapan multi-file, server mungkin mencoba memperbarui dirinya sendiri, dan kemudian Anda akan memiliki server yang rusak.
Merlyn Morgan-Graham
2

Jika Anda membangun aplikasi Anda menggunakan plugin Equinox , Anda dapat menggunakan Sistem Penyediaan P2 untuk mendapatkan solusi siap pakai untuk masalah ini. Ini akan mengharuskan server untuk memulai ulang sendiri setelah pembaruan.

Tom Crockett
sumber
1

Saya melihat masalah keamanan saat mengunduh jar baru (dll.), Misalnya, seorang pria di tengah serangan. Anda selalu harus menandatangani pembaruan yang dapat diunduh.

Pada JAX2015, Adam Bien bercerita tentang penggunaan JGit untuk memperbarui binari. Sayangnya saya tidak dapat menemukan tutorial apapun.

Sumber dalam bahasa Jerman.

Adam Bien menciptakan pembaru, lihat di sini

Saya bercabang di sini dengan beberapa frontend javaFX. Saya juga sedang mengerjakan penandatanganan otomatis.

Kaito
sumber