visor - oracle java web
¿Cómo gestiona los archivos de configuración incrustados y las bibliotecas en las aplicaciones web de Java? (5)
Actualmente estoy trabajando en un proyecto j2ee que ha estado en beta desde hace un tiempo. En este momento solo estamos solucionando algunos de los problemas con el proceso de implementación. Específicamente, hay una serie de archivos incrustados en la guerra (algunos archivos xml y .properties) que necesitan versiones diferentes que se implementan dependiendo de si se encuentra en un entorno de desarrollo, prueba o desarrollo. Cosas como loglevels, pools de conexión, etc.
Así que me preguntaba cómo los desarrolladores aquí estructuran su proceso para implementar webapps. ¿Descargan tanta configuración como pueden al servidor de aplicaciones? ¿Reemplaza los archivos de configuración mediante programación antes de implementar? Elija una versión durante el proceso de compilación? Edita manualmente las guerras?
Además, ¿qué tan lejos puede llegar a proporcionar dependencias a través de las bibliotecas estáticas de los servidores de aplicaciones y cuánto invierten ellos mismos en la guerra? Todo esto solo para obtener algunas ideas de cuál es la práctica común (o tal vez mejor) en este momento.
Creo que si las propiedades son específicas de la máquina / implementación, entonces pertenecen a la máquina. Si voy a cerrar las cosas en una guerra, debería ser insertable, lo que no significa nada específico para la máquina en la que se está ejecutando. Esta idea se romperá si la guerra tiene propiedades dependientes de la máquina.
Lo que me gusta hacer es construir un proyecto con un archivo properties.example, cada máquina tiene una .properties que vive en algún lugar donde la guerra puede acceder.
Una forma alternativa sería tener tareas de hormiga, por ejemplo, para la guerra de desarrollo, guerra de etapas, guerra prodigiosa y tener los conjuntos de propiedades como parte del proyecto, integrados en la construcción de guerra. No me gusta tanto porque terminará teniendo cosas como ubicaciones de archivos en un servidor individual como parte de la compilación de su proyecto.
Puse toda la configuración en la base de datos. El contenedor (Tomcat, WebSphere, etc.) me da acceso a la conexión de la base de datos inicial y, a partir de ese momento, todo sale de la base de datos. Esto permite múltiples entornos, clustering y cambios dinámicos sin tiempo de inactividad (o al menos sin una nueva implementación). Especialmente agradable es poder cambiar el nivel de registro sobre la marcha (aunque necesitará una pantalla de administración o una actualización de fondo para recoger los cambios). Obviamente, esto solo funciona para cosas que no son necesarias para iniciar la aplicación, pero en general, puede acceder a la base de datos bastante rápido después del inicio.
Normalmente hago dos archivos de propiedades:
- uno para las especificaciones de la aplicación (mensajes, palabras "mágicas" internas) integradas en la aplicación,
- el otro para las especificaciones del entorno (acceso a db, niveles de registro y rutas ...) expuestas en el classpath de cada servidor y "adheridas" (no entregadas con mi aplicación). Por lo general, "mago" o "anticipo" a estos para poner valores específicos, dependiendo del entorno de destino.
- Los chicos geniales usan JMX para mantener su conf de la aplicación (conf se puede modificar en tiempo real, sin volver a desplegar), pero es demasiado complejo para mis necesidades.
Bibliotecas del servidor (¿estático?): Desaconsejo firmemente el uso de la biblioteca del servidor en mis aplicaciones, ya que agrega dependencia al servidor:
- OMI, mi aplicación debe ser "autoenvasada": abandonar mi guerra, y eso es todo. He visto guerras con 20 Mbs de jarras, y eso no me molesta.
- Una práctica recomendada común es limitar sus dependencias externas a lo que ofrece el dogma J2EE: la API J2EE (uso de Servlets, Ejbs, Jndi, JMX, JMS ...). Tu aplicación debe ser "independiente del servidor".
- Poner dependencias en su aplicación (war, ear, wathever) es autodocumentado: usted sabe de qué bibliotecas depende su aplicación. Con las bibliotecas de servidores, debe documentar claramente estas dependencias, ya que son menos obvias (y pronto sus desarrolladores olvidarán esta pequeña magia).
- Si actualiza su servidor de aplicaciones, las posibilidades de que la lib del servidor de la que depende también cambie. Se supone que los editores de AppServer no mantienen la compatibilidad en sus libs internas de una versión a otra (y la mayoría de las veces no lo hacen).
- Si usa una lib ampliamente utilizada incorporada en su appServer (se le viene a la mente el registro jakarta commons, también conocido como jcl) y desea actualizar su versión para obtener las últimas características, asume el enorme riesgo de que su servidor de aplicaciones no lo admita.
- Si confía en un objeto de servidor estático (en un campo estático de una clase de servidor, por ejemplo, un Mapa o un registro), tendrá que reiniciar su servidor de aplicaciones para limpiar este objeto. Pierdes la posibilidad de volver a implementar tu aplicación en caliente (el objeto del servidor antiguo seguirá existiendo entre redespliegues). El uso de objetos de todo el servidor de aplicaciones (distintos de los definidos por J2EE) puede conducir a errores sutiles, especialmente si este objeto se comparte entre varias aplicaciones. Es por eso que desaconsejo encarecidamente el uso de objetos que residen en un campo estático de una AppServer lib.
Si necesitas "este objeto en el jar de este servidor de aplicaciones", intenta copiar el contenedor en tu aplicación, esperando que no haya dependencia en el jar de otro servidor, y verificando la política de carga de clases de tu aplicación (tomo el hábito de poner un "último padre" cargando) política en todas mis aplicaciones: estoy seguro de que no estaré "contaminado" por las jarras del servidor, pero no sé si es una "mejor práctica").
Trabajo en un entorno en el que un equipo de servidores por separado realiza la configuración de los servidores de control de calidad y producción para nuestras aplicaciones. Cada aplicación generalmente se implementa en dos servidores en QA y tres servidores en Production. Mi equipo de desarrollo ha descubierto que es mejor minimizar la cantidad de configuración requerida en el servidor al poner tanta configuración como sea posible en la guerra (u oído). Esto facilita la configuración del servidor y también minimiza la posibilidad de que el equipo del servidor configure incorrectamente el servidor.
No tenemos una configuración específica de la máquina, pero sí tenemos una configuración específica del entorno (Dev, QA y Producción). Tenemos archivos de configuración almacenados en el archivo war que son nombrados por el entorno (por ejemplo, dev.properties, qa.properties, prod.properties). Ponemos una propiedad -D en la línea de comandos java de la máquina virtual del servidor para especificar el entorno (por ejemplo, java -Dapp.env = prod ...). La aplicación puede buscar la propiedad del sistema app.env y usarla para determinar el nombre del archivo de propiedades que se usará.
Supongo que si tiene un pequeño número de propiedades específicas de la máquina, entonces también podría especificarlas como propiedades -D. La configuración de Commons proporciona una manera fácil de combinar archivos de propiedades con propiedades del sistema.
Configuramos pools de conexiones en el servidor. Llamamos al grupo de conexiones el mismo para cada entorno y simplemente señalamos los servidores que están asignados a cada entorno a la base de datos apropiada. La aplicación solo debe conocer el nombre de un grupo de conexiones.
Archivos de configuración wrt, creo que la respuesta de Steve es la mejor hasta el momento. Agregaría la sugerencia de hacer que los archivos externos se relacionen con la ruta de instalación del archivo de guerra, de esa manera puede tener múltiples instalaciones de la guerra en el único servidor con diferentes configuraciones.
por ejemplo, si mi dev.war
se desempaqueta en /opt/tomcat/webapps/dev
, entonces usaría ServletContext.getRealPath
para encontrar la carpeta base y el nombre de la carpeta war, por lo que los archivos de configuración vivirían en ../../config/dev
relativo a la guerra, o /opt/tomcat/config/dev
para absoluto.
También estoy de acuerdo con Bill sobre poner tan poco como sea posible en estos archivos de configuración externos. Usar la base de datos o JMX dependiendo de su entorno para almacenar todo lo que tenga sentido. Apache Commons Configuration tiene un buen objeto para manejar configuraciones respaldadas por una tabla de base de datos.
En cuanto a las bibliotecas, estoy de acuerdo con unknown al tener todas las libs en la carpeta WEB-INF/lib
en el archivo war (self-packaged). La ventaja es que cada instalación de la aplicación es autónoma, y puede tener diferentes versiones de la guerra utilizando diferentes versiones de las bibliotecas al mismo tiempo.
La desventaja es que utilizará más memoria ya que cada aplicación web tendrá su propia copia de las clases, cargada por su propio cargador de clases.
Si esto representa una preocupación real, puede colocar los archivos jar en la carpeta común de la biblioteca para su contenedor de servlets ( $CATALINA_HOME/lib
para tomcat). Todas las instalaciones de su aplicación web que se ejecutan en el mismo servidor tienen que usar las mismas versiones de las bibliotecas. (En realidad, eso no es estrictamente cierto ya que podría poner versiones de reemplazo en la carpeta WEB-INF/lib
individual si es necesario, pero eso se está volviendo bastante complicado de mantener).
Construiría un instalador automatizado para las bibliotecas comunes en este caso, usando InstallShield o NSIS o su equivalente para su sistema operativo. Algo que puede hacer que sea más fácil saber si tiene el conjunto de bibliotecas más actualizado, y la actualización, la degradación, etc.