¿Cómo puedo escribir una aplicación Java que pueda actualizarse en tiempo de ejecución?

91

Me gustaría implementar una aplicación java (aplicación de servidor) que pueda descargar una nueva versión (archivo .jar) de una URL determinada y luego actualizarse en tiempo de ejecución.

¿Cuál es la mejor manera de hacer esto? ¿Es posible?

Supongo que la aplicación puede descargar un nuevo archivo .jar e iniciarlo. Pero, ¿cómo debo hacer el traspaso? Por ejemplo, saber cuándo se inicia la nueva aplicación y luego salir. ¿O hay una mejor manera de hacer esto?

Jonas
fuente
4
¿Ha mirado Java Web Start? Sin embargo, creo que no se actualiza en tiempo de ejecución (es necesario reiniciar), para eso probablemente debas mirar OSGi.
Thilo
@Thilo: Creo que sería fácil descargar un archivo desde una URL determinada y luego iniciarlo con un comando de Linux desde el archivo jar en ejecución.
Jonas
1
El diseño de la API Java WebStart hace que sea imposible actualizar mientras se ejecuta. Desafortunadamente.
Thorbjørn Ravn Andersen
jefe @meain que la roca, que hizo que mi día :)
iltaf Khalid

Respuestas:

68

La estructura básica de una solución es la siguiente:

  • Hay un bucle principal responsable de cargar repetidamente la última versión de la aplicación (si es necesario) y ejecutarla.

  • La aplicación hace lo suyo, pero comprueba periódicamente la URL de descarga. Si detecta una nueva versión, vuelve al lanzador.

Hay varias formas de implementar esto. Por ejemplo:

  • El lanzador podría ser un script contenedor o una aplicación binaria que inicia una nueva JVM para ejecutar la aplicación desde un archivo JAR que se reemplaza.

  • El lanzador podría ser una aplicación Java que crea un cargador de clases para el nuevo JAR, carga una clase de punto de entrada y llama a algún método en ella. Si lo hace de esta manera, debe estar atento a las fugas de almacenamiento del cargador de clases, pero eso no es difícil. (Solo debe asegurarse de que no se pueda acceder a ningún objeto con clases cargadas desde el JAR después de reiniciar).

Las ventajas del enfoque de envoltura externa son:

  • solo necesitas un JAR,
  • puede reemplazar toda la aplicación Java,
  • cualquier subproceso secundario creado por la aplicación, etc. desaparecerá sin una lógica de apagado especial, y
  • también puede lidiar con la recuperación de fallas de aplicaciones, etc.

El segundo enfoque requiere dos JAR, pero tiene las siguientes ventajas:

  • la solución es Java puro y portátil,
  • el cambio será más rápido y
  • puede retener el estado más fácilmente durante el reinicio (problemas de fugas de módulo).

La "mejor" forma depende de sus requisitos específicos.

También debe tenerse en cuenta que:

  • Existen riesgos de seguridad con la actualización automática. En general, si el servidor que proporciona las actualizaciones se ve comprometido, o si los mecanismos para proporcionar las actualizaciones son susceptibles de ser atacados, la actualización automática puede llevar a comprometer a los clientes.

  • Enviarle una actualización a un cliente que cause daño al cliente podría tener riesgos legales y riesgos para la reputación de su empresa.


Si puede encontrar una manera de evitar reinventar la rueda, sería bueno. Consulte las otras respuestas para obtener sugerencias.

Esteban C
fuente
43

Actualmente estoy desarrollando un JAVA Linux Daemon y también tuve la necesidad de implementar un mecanismo de actualización automática. Quería limitar mi aplicación a un archivo jar y se me ocurrió una solución simple:

Empaquete la aplicación de actualización en la propia actualización.

Aplicación : cuando la aplicación detecta una versión más reciente, hace lo siguiente:

  1. Descargar actualización (Zipfile)
  2. Extraer aplicación y ApplicationUpdater (todo en el archivo zip)
  3. Ejecutar actualizador

ApplicationUpdater : cuando se ejecuta el actualizador, hace lo siguiente:

  1. Detener la aplicación (en mi caso, un demonio a través de init.d)
  2. Copie el archivo jar descargado para sobrescribir la aplicación actual
  3. Iniciar la aplicación
  4. Limpiar.

Espero que ayude a alguien.

Berdus
fuente
12

Este es un problema conocido y recomiendo no reinventar una rueda: no escriba su propio truco, solo use lo que otras personas ya han hecho.

Dos situaciones que debes considerar:

  1. La aplicación debe ser autoactualizable y seguir ejecutándose incluso durante la actualización (aplicación de servidor, aplicaciones integradas). Vaya con OSGi: Bundles o Equinox p2 .

  2. La aplicación es una aplicación de escritorio y tiene un instalador. Hay muchos instaladores con opción de actualización. Consulte la lista de instaladores .

Peter Knego
fuente
12

Recientemente he creado update4j que es totalmente compatible con el sistema de módulos de Java 9.

Iniciará sin problemas la nueva versión sin reiniciar.

Mordejai
fuente
Mediante el uso de un paradigma de aplicación de negocios / bootstrap. El bootstrap carga y descarga la aplicación empresarial y es el bootstrap el que generalmente realiza la actualización.
Mordejai
10

Escribí una aplicación Java que puede cargar complementos en tiempo de ejecución y comenzar a usarlos de inmediato, inspirada en un mecanismo similar en jEdit. jEdit es de código abierto, por lo que tiene la opción de ver cómo funciona.

La solución utiliza un ClassLoader personalizado para cargar archivos desde el jar. Una vez que estén cargados, puede invocar algún método del nuevo jar que actuará como su mainmétodo. Luego, la parte complicada es asegurarse de eliminar todas las referencias al código antiguo para que pueda ser recolectado como basura. No soy un experto en esa parte, lo hice funcionar pero no fue fácil.

Brad Mace
fuente
No sé sobre Java, pero en C #, puede usar AppDomains para descargar código explícitamente. Quizás haya un concepto similar en Java.
Merlyn Morgan-Graham
1
Gracias, este parece ser el camino a seguir. Hay un ejemplo de NetworkClassLoaderen JavaDoc para ClassLoader
Jonas
¿Tuvo problemas con las variables estáticas dentro de la misma JVM, o qué pasa con la generación permanente? He escuchado que estos pueden presentar problemas con respecto a la descarga de clases.
ide
5
  1. Primera forma: use tomcat y sus instalaciones de implementación.
  2. Segunda forma: dividir la aplicación en dos partes (funcional y actualización) y dejar que la parte de actualización reemplace la parte de función.
  3. Tercera forma: en la aplicación de su servidor, simplemente descargue la nueva versión, luego la versión anterior libera el puerto vinculado, luego la versión anterior ejecuta la nueva versión (inicia el proceso), luego la versión anterior envía una solicitud en el puerto de la aplicación a la nueva versión para eliminar la versión anterior, la versión anterior termina y la nueva versión elimina la versión anterior. Me gusta esto: texto alternativo
jnr
fuente
Tu segunda forma parece ser un diseño interesante.
Jonas
2

Esta no es necesariamente la mejor manera, pero podría funcionar para usted.

Puedes escribir una aplicación de arranque (como el lanzador de World of Warcraft, si has jugado WoW). Ese bootstrap es responsable de buscar actualizaciones.

  • Si hay una actualización disponible, la ofrecerá al usuario, manejará la descarga, instalación, etc.
  • Si la aplicación está actualizada, permitirá al usuario iniciar la aplicación.
  • Opcionalmente, puede permitir que el usuario inicie la aplicación, incluso si no está actualizada.

De esta manera, no tiene que preocuparse por forzar la salida de su aplicación.

Si su aplicación está basada en la web, y si es importante que tengan un cliente actualizado, también puede verificar la versión mientras se ejecuta la aplicación. Puede hacerlo a intervalos, mientras realiza una comunicación normal con el servidor (algunas o todas las llamadas), o ambas.

Para un producto en el que trabajé recientemente, realizamos verificaciones de versión al momento del lanzamiento (sin una aplicación de arranque, pero antes de que apareciera la ventana principal) y durante las llamadas al servidor. Cuando el cliente estaba desactualizado, confiamos en que el usuario saliera manualmente, pero prohibimos cualquier acción contra el servidor.

Tenga en cuenta que no sé si Java puede invocar el código de la interfaz de usuario antes de que aparezca la ventana principal. Estábamos usando C # / WPF.

Merlyn Morgan-Graham
fuente
Gracias, es una buena forma de hacerlo. Pero es una aplicación de servidor, por lo que no hay ningún usuario que pueda realizar alguna acción. Y preferiría que sea solo un archivo jar, por lo que el usuario easyli puede descargar un solo jar e iniciarlo desde el principio, pero después de eso me gustaría no tener interacciones con el usuario.
Jonas
Cuando dice "aplicación de servidor", ¿quiere decir que es una aplicación que se ejecuta directamente en el servidor, mientras está conectado?
Merlyn Morgan-Graham
@Jonas: No entiendo mucho sobre cómo funcionan los archivos .jar, así que no puedo ser de mucha utilidad allí. Puedo entender si prefiere una respuesta específica para Java. Con suerte, esto le dará que pensar, al menos :)
Merlyn Morgan-Graham
@Merlyn: Estoy agradecida por compartir tus ideas. Sí, es una aplicación Java que se ejecutará en un servidor Linux y nadie ha iniciado sesión en ese servidor.
Jonas
1
@Jonas: No es que no hayas pensado en esto, pero la mayoría de los servicios web que he visto tienen una estrategia de implementación manual. Es posible que desee tener cuidado al buscar actualizaciones. Si inserta un código defectuoso / roto, o solo lo hace parcialmente con una implementación de múltiples archivos, el servidor podría intentar actualizarse a sí mismo, y luego tendría un servidor roto.
Merlyn Morgan-Graham
2

Si crea su aplicación utilizando complementos de Equinox , puede usar el sistema de aprovisionamiento P2 para obtener una solución lista para este problema. Esto requerirá que el servidor se reinicie después de una actualización.

Tom Crockett
fuente
1

Veo un problema de seguridad al descargar un nuevo jar (etc.), por ejemplo, un hombre en el medio del ataque. Siempre tienes que firmar tu actualización descargable.

En JAX2015, Adam Bien habló sobre el uso de JGit para actualizar los binarios. Lamentablemente no pude encontrar ningún tutorial.

Fuente en alemán.

Adam Bien creó el actualizador ver aquí

Lo bifurqué aquí con una interfaz javaFX. También estoy trabajando en una firma automática.

Kaito
fuente