2009-09-21 13 views
31

Tenemos un sistema Java grande (> 500,000 LOC) que depende de 40-50 paquetes OSS . El sistema está construido con Ant, y la administración de dependencias es manejada manualmente en este momento. Estoy investigando Ivy y/o Maven al automatizar dependencias. Miramos a Maven como un sistema de automatización de compilación el año pasado y lo rechazamos porque requeriría totalmente reestructurar nuestro sistema para que coincida con la arquitectura de Maven. Ahora estoy buscando automatizar solo las tareas de administración de dependencias.Gran gestión de dependencias del sistema Java

He hecho algunos experimentos con Ivy pero he tenido problemas. Por ejemplo, cuando especifico ActiveMQ como una dependencia y le digo a Ivy que use los POM en el repositorio Maven para la especificación de dependencia, Ivy recupera un grupo de paquetes (Jetty, Derby y Geronimo para la instancia ) que sé que no son No era necesario usar solo ActiveMQ.

Si fijo usepoms = "false" en ivysettings.xml se obtiene sólo activemq.jar, pero que parece frustrar el propósito de hiedra y relega a un simple frasco-fetcher con manualmente incorporadas dependencia especificaciones .

Hay un problema mayor aquí, lo que solía llamarse "DLL Hell" en Windows. En algunos casos, dos dependencias directas de primer nivel indicarán diferentes versiones de la misma dependencia transitiva (para instancia log4j.jar). Solo un log4j.jar puede estar en el classpath, por lo que la resolución de dependencia implica determinar manualmente qué versión es compatible con todos sus clientes en nuestro sistema.

Supongo que todo se reduce a la calidad de cada paquete de dependencia especificación (el POM). En el caso de ActiveMQ, no hay declaraciones de alcance , por lo que cualquier referencia a ActiveMQ descargará todas sus dependencias a menos que excluyamos manualmente las que sabemos que no queremos .

En el caso de log4j, la resolución automática de dependencias requeriría que todos los clientes de log4j (otros paquetes que dependen de log4j) validar contra todas las versiones anteriores de log4j y proporcionan un rango (o lista) de las versiones de log4j compatibles en el POM. Probablemente también sea demasiado para preguntar.

¿Es este el estado actual de las cosas, o me falta algo?

+8

+1 para una descripción clara del problema con los intentos de solución. Espero que tengas una buena respuesta. –

+2

Las 4 mejores respuestas upvoted proporcionan una perspectiva valiosa. Kevin es conciso y preciso. Robert y Rich proporcionan muchos más detalles. Vladimir aporta una opinión positiva basada en la experiencia del mundo real. Los cuatro juntos me ayudan a establecer mis expectativas y señalar el camino hacia la solución del problema. Me gustaría "aceptar" las cuatro respuestas, pero SO no lo permite. Le doy la palabra a Robert Munteanu porque fue el primero con una respuesta detallada. –

Respuesta

11

Tienes toda la razón al decir que

Supongo que todo se reduce a la calidad de la especificación de dependencia de cada paquete (el POM).

Lo único que quiero agregar es ver el POM, o cualquier otra forma de metadatos, como punto de partida. Es bastante útil que, p. ActiveMQ proporciona todas las dependencias por usted, pero depende de usted elegir si realmente se ajusta a su proyecto.

Después de todo, incluso teniendo en cuenta la versión de log4j, ¿tendría dependencias externas para elegir la versión o elegir la versión que sabe que funciona para usted?


En cuanto a cómo se puede optar por las dependencias de la medida, esto es lo que puede hacer con la hiedra:

paquetes innecesarios

Ivy recupera un montón de paquetes (el embarcadero, Derby y Geronimo de instancia) que sé que no son necesarios para usar ActiveMQ.

Esto suele ocurrir debido a la poca modularidad de la aplicación. Algunas partes de la aplicación necesitan Jetty por ejemplo, pero terminas con esta dependencia transitiva incluso si no la usas.

Es probable que desee ver en los ivy exclude mechanism:

<dependency name="A" rev="1.0"> 
    <exclude module="B"/> 
</dependency> 

versiones de dependencia

Sólo una log4j.jar puede estar en la ruta de clases, por lo que la dependencia resolución implica determinar manualmente qué versión es compatible con todos sus clientes en nuestro sistema.

Tal vez estoy leyendo mal esto, pero no hay manual de elemento en la resolución de conflictos de Ivy. Hay una lista de default conflict managers:

  • todo: esto entra en conflicto conflictos Administrador de solución mediante la selección de todas las revisiones. También llamado NoConflictManager, desaloja cualquier módulo.
  • última vez: este administrador de conflictos selecciona solo la "última" revisión, la última se define como la última en el tiempo. Tenga en cuenta que lo último en el tiempo es costoso de calcular, por lo tanto, prefiera la última revisión si puede.
  • última revisión: este administrador de conflictos selecciona solo la "última" revisión, siendo la última definida por una comparación de cadenas de revisiones.
  • último-compatible: este administrador de conflictos selecciona la última versión en los conflictos que pueden dar lugar a un conjunto compatible de dependencias. Esto significa que al final este administrador de conflictos no permite ningún conflicto (como el administrador de conflictos estrictos), excepto que sigue una estrategia de mejor esfuerzo para tratar de encontrar un conjunto de módulos compatibles (de acuerdo con las restricciones de versión);
  • strict: este administrador de conflictos arroja una excepción (es decir, causa un error de compilación) cada vez que se encuentra un conflicto.

Si es necesario, puede provide your own conflict manager.

-5

"¿Es este el actual estado de cosas"

Sí.

Este es el compromiso de fuente abierta.

Un marco de código cerrado (es decir, .Net) resolverá todo esto por usted.

Una solución de código abierto significa que debe resolverla (y resolverla) todo el tiempo.

Es posible que pueda encontrar algunas configuraciones preconstruidas y pagar a alguien para mantenerlas al día. Por ejemplo, puede elegir usar Red Hat Enterprise Linux. Si se ajusta exactamente a lo que admiten (y nada más), entonces la configuración se resuelve.

Las probabilidades son buenas, sin embargo, que ninguna configuración empaquetada cumpla con sus requisitos.

+2

Estoy totalmente en desacuerdo con su código cerrado es mejor, de código abierto está invirtiendo esfuerzo constantemente. ¿Tiene una alternativa de código cerrado para este problema? Excepto .NET. –

+1

No estoy de acuerdo en que este sea el estado de las cosas. Las dependencias en cuestión son opcionales y se gestionan correctamente en Maven (ver mi respuesta para más detalles). Puede ser que Ivy no pueda ocuparse de la propiedad opcional, pero todavía tiene un medio para tratar con esas dependencias. Entonces, ¿cuál es su base para hacer este reclamo? –

+0

@Robert Munteanu: No dije que una solución de código cerrado fuera mejor de una manera vaga y general. Proporciona componentes preintegrados. Esa es su * única * ventaja. Y la mayor parte del tiempo, eso es realmente una responsabilidad porque las cosas preintegradas evolucionan lentamente y van a la zaga del estado del arte. –

4

Creo que este es de hecho el estado actual de las cosas. OSGi y el nuevo sistema de empaquetado propuesto para Java 1.7 (¿ya se ha llegado a un acuerdo sobre esto?) Son intentos de solucionar al menos la cuestión de depender de diferentes versiones de una biblioteca, pero no lo hago Creo que podrán solucionar su problema ahora mismo.

+0

¿tiene algún vínculo con más información sobre "el nuevo sistema de embalaje propuesto para Java 1.7"? –

+0

Estaba pensando en JSR 277: http://jcp.org/en/jsr/detail?id=277 - google para un montón de opiniones ;-) ... Yo _pensé_ esto estaba destinado a entrar en 1.7 pero aparentemente no va a suceder –

2

"¿Es este el estado actual de las cosas?"

No con OSGi. Es posible que desee ver contenedores y paquetes OSGi. Un paquete es como un archivo jar, pero admite metadatos que detallan su versión y las versiones de los paquetes relacionados que requiere (colocando atributos en el archivo META-INF). Por lo tanto, su paquete log4j indicará su versión, y los paquetes dependientes detallarán qué versiones de log4j requieren.

Además, se admite un mecanismo de carga de clases no jerárquico, de modo que puede cargar varias versiones de log4j, y diferentes paquetes pueden especificar y vincularse a esas versiones diferentes.

Javaworld tiene una muy buena introducción here.

5

Eso es todo. El sistema de dependencia maven (que Ivy sigue más o menos) deja en manos de los proyectos individuales hacer un buen trabajo al agregar los metadatos necesarios para sus dependencias. La mayoría no.

Si va por esa ruta, espere pasar el tiempo configurando las exclusiones.

Para los carteles que recomiendan OSGi, el PO dice que no está dispuesto a modificar la arquitectura de su sistema de construcción para Maven, no pensaría que querría volver a diseñar su aplicación ser compatible con OSGi. Por otra parte, una gran cantidad de proyectos de software que son compatibles OSGi (y no son tantos como se esperaría) tiene tan malo o peor que en los metadatos Maven

+0

Creo que decir "La mayoría no" es una gran exageración, ¿puedes citar algún proyecto importante que esté mal configurado? La gran mayoría de los proyectos de Maven que he usado se comportan bien. Es más cierto que la mayoría de los usuarios no entienden todos los matices del sistema y se tropiezan con ellos. Todavía puede ser un problema si los usuarios no usan RTFM, pero ese es siempre el caso con sistemas complejos. –

+0

Ivy no "sigue en su mayoría" el sistema de dependencia Maven, simplemente tiene un sistema de adaptador para él. Es cierto que los POM importados de Maven son una mierda, pero si te tomas el tiempo para volver a hacer manualmente las configuraciones de dependencia de Ivy, en realidad terminas con algo mucho mejor que Maven podría proporcionar. – Esko

+0

@Rich ¿Aparte de ActiveMQ que notó el OP? He tenido problemas con CXF que no declara correctamente las dependencias como "opcional" o de lo contrario no es necesario. – Kevin

5

De las dependencias que enumera, las siguientes se definen como optional en el activemq-core pom (vea también el relevant section del libro de Maven).

  • org.apache.derby: derby
  • org.apache.geronimo.specs: geronimo-jta_1.0.1B_spec

no vi una dependencia directa en el embarcadero, por lo que puede ser incluido transitivamente desde una de las dependencias opcionales.

En Maven, las dependencias opcionales se manejan automáticamente. Esencialmente, cualquier dependencia declarada como opcional debe ser redeclarada en su pom para que sea utilizada.De la documentación vinculada arriba:

Las dependencias opcionales se usan cuando no es realmente posible (por la razón que sea) dividir un proyecto en submódulos. La idea es que algunas de las dependencias solo se utilizan para ciertas funciones del proyecto y no serán necesarias si no se utiliza esa función. Idealmente, dicha característica se dividiría en un submódulo que dependía del proyecto de funcionalidad central ... este nuevo subproyecto tendría solo dependencias no opcionales, ya que las necesitaría todas si decidiera usar la funcionalidad del subproyecto.

Sin embargo, como el proyecto no se puede dividir (nuevamente, por el motivo que sea), estas dependencias se declaran opcionales. Si un usuario desea utilizar la funcionalidad relacionada con una dependencia opcional, deberá redeclarar esa dependencia opcional en su propio proyecto. Esta no es la forma más clara de manejar esta situación, pero de nuevo tanto las dependencias opcionales como las exclusiones de dependencia son soluciones intermedias.

No estoy seguro de si puede configurar Ivy para ignorar las dependencias opcionales, pero puede configurarlo en exclude dependencies. Por ejemplo:

<dependency name="A" rev="1.0"> 
    <exclude module="B"/> 
</dependency> 

Esto no es del todo satisfactorio, lo sé. Puede ser que Ivy sea compatible con dependencias opcionales (lo veré más y actualizaré si encuentro algo), pero el mecanismo de exclusiones al menos te permite administrarlas.


En la última parte de su pregunta. Maven resolverá las versiones de dependencia para log4j y, si las versiones son compatibles, seleccionará automáticamente la "más cercana" de las versiones enumeradas.

Desde el Introduction to the Dependency Mechanism:

  • Dependencia de mediación - Determina lo que se utilizará la versión de una dependencia cuando se encuentran varias versiones de un artefacto. Actualmente, Maven 2.0 solo admite el uso de la "definición más cercana", lo que significa que utilizará la versión de la dependencia más cercana a su proyecto en el árbol de dependencias. Siempre puede garantizar una versión al declararla explícitamente en el POM de su proyecto. Tenga en cuenta que si dos versiones de dependencia tienen la misma profundidad en el árbol de dependencias, hasta Maven 2.0.8 no se definió cuál ganaría, pero desde Maven 2.0.9 es el orden en la declaración lo que cuenta: la primera declaración gana.

    • "definición más cercana" significa que la versión utilizada será la más cercana a su proyecto en el árbol de dependencias, por ej. si las dependencias para A, B y C se definen como A -> B -> C -> D 2.0 y A -> E -> D 1.0, entonces D 1.0 se usará cuando se construya A porque la ruta de A a D a través E es más corto. Se podría añadir una dependencia explícita a D 2.0 en A para forzar el uso de D 2,0

Cuando las versiones no son compatibles tiene un problema más grande que la dependencia resolución. Creo que Ivy opera con un modelo similar, pero yo no soy un experto.

3

Actualmente estoy usando Ivy para administrar más de 120 OSS y bibliotecas propietarias para varios proyectos (algunos autónomos, algunos dependientes). En 2005 (cuando Ivy todavía era de Jayasoft) decidí (o tuve que) escribir los archivos ivy.xml para cada paquete integrado.

La mayor ventaja es que tengo control total sobre las diversas configuraciones. Esto puede sonar excesivo para algunos, pero nuestro sistema de compilación ha estado funcionando confiablemente durante más de 4 años y agregar una nueva biblioteca es generalmente un trabajo de 5 minutos.

0

Existe toda la idea de la inyección de dependencia, lo que invariablemente llevaría a que el programa se reestructure. He escuchado algo de ruido acerca de que GUICE es bueno en este sentido. En la perspectiva de implementación, he tenido un éxito razonable al implementar solo el .jar que creamos con los .jars de la dependencia que se obtienen de los proyectos originales a través de jnlp. El sistema de compilación detrás de esto implicó el seguimiento manual de las nuevas versiones de las dependencias y la actualización en el sistema de compilación.

Cuestiones relacionadas