what compile better java maven-2 build-process dependencies ivy

java - compile - what is better gradle or maven



Administración de grandes dependencias del sistema Java (8)

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 distintas 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.

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 se maneja manualmente en la actualidad. Estoy investigando Ivy y / o Maven para automatizar dependencias. Miramos a Maven como un sistema de automatización de compilación el año pasado y lo rechazamos porque requeriría una reestructuración total de nuestro sistema para que coincida con la arquitectura de Maven. Ahora estoy buscando automatizar solo las tareas de administración de dependencias.

He hecho algunos experimentos con Ivy, pero me he encontrado con problemas. Por ejemplo, cuando especifico ActiveMQ como una dependencia, y le digo a Ivy que use los POM en el repositorio de Maven para la especificación de dependencia, Ivy recupera un montón de paquetes (Jetty, Derby y Geronimo, por ejemplo) que sé que no son necesarios solo para utilizar ActiveMQ.

Si configuro usepoms = "false" en ivysettings.xml, solo obtiene activemq.jar, pero eso parece anular el propósito de Ivy y lo relega a un simple jar-fetcher con especificaciones de dependencia creadas manualmente.

Aquí hay un problema mayor, lo que se solía llamar "DLL Hell" en Windows. En algunos casos, dos dependencias directas de primer nivel apuntarán a versiones diferentes de la misma dependencia transitiva (por ejemplo, log4j.jar). Solo un log4j.jar puede estar en la ruta de clase, 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 la especificación de dependencia de cada paquete (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 deseamos.

En el caso de log4j, la resolución de dependencia automática requeriría que todos los clientes de log4j (otros paquetes que dependen de log4j) se validen con todas las versiones anteriores de log4j y proporcionen un rango (o lista) de versiones de log4j compatibles en el POM. Eso es probablemente demasiado pedir.

¿Es esta la situación actual, o me estoy perdiendo algo?


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 este punto?) Son intentos de solucionar al menos la cuestión de depender de diferentes versiones de una biblioteca, pero no lo hago. Piensa que podrán solucionar tu problema ahora mismo.


De las dependencias que enumera, las siguientes se definen como optional en activemq-core pom (vea también la sección correspondiente del libro de Maven).

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

No vi una dependencia directa en Jetty, por lo que puede estar incluida de forma pasiva 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 enlazada arriba:

Las dependencias opcionales se usan cuando en realidad no es posible (por el motivo 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, dado que el proyecto no se puede dividir (de nuevo, 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 nuevamente, tanto las dependencias opcionales como las exclusiones de dependencias son soluciones de emergencia.

No estoy seguro de si puede configurar Ivy para ignorar las dependencias opcionales, pero puede configurarlo para excluir dependencias . 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.

Respecto a la última parte de tu 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.

Del Mecanismo de Introducción al Dependencia :

  • Mediación de dependencia: esto determina qué versión de una dependencia se usará cuando se encuentren múltiples 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 ejemplo. 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. Podría agregar explícitamente una dependencia a D 2.0 en A para forzar el uso de D 2.0

Donde las versiones no son compatibles, tiene un problema mayor que la resolución de dependencia. Creo que Ivy opera en un modelo similar pero no soy un experto.


Eso es practicamente 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 lo hacen.

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

Para los carteles que recomiendan OSGi, el OP dijo que no está dispuesto a rediseñar su sistema de compilación para Maven, no creo que quiera volver a diseñar su aplicación para que sea compatible con OSGi. Además, muchos proyectos OSS que cumplen con OSGi (y no hay tantos como cabría esperar) tienen metadatos tan malos o peores que en Maven.


Existe la idea general de la inyección de dependencia, que invariablemente llevaría a la reestructuración del programa. He estado escuchando un poco de ruido sobre GUICE siendo 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 involucró el seguimiento manual de nuevas versiones de dependencias y la actualización en el sistema de compilación.


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 agregaría es ver el POM, o cualquier otra forma de metadatos, como punto de partida. Es muy útil que, por ejemplo, ActiveMQ le proporcione todas las dependencias, pero depende de usted elegir si realmente se adapta a su proyecto.

Después de todo, incluso teniendo en cuenta la versión log4j, ¿tendrías dependencias externas que eligieran la versión o la versión que sabes que funciona para ti?

En cuanto a cómo puede elegir adaptar las dependencias, esto es lo que puede hacer con Ivy:

Paquetes innecesarios

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

Esto generalmente sucede debido a la modularidad pobre en la aplicación. Alguna parte de la aplicación necesita Jetty, por ejemplo, pero terminas con esta dependencia transitiva incluso si no la usas.

Probablemente quiera ver el mecanismo de exclusión de hiedra :

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

Versiones de dependencia

Solo un log4j.jar puede estar en el classpath, por lo que la resolución de la dependencia implica determinar manualmente qué versión es compatible con todos sus clientes en nuestro sistema.

Tal vez estoy malinterpretando esto, pero no hay un elemento manual en la resolución de conflictos de Ivy. Hay una lista de administradores de conflictos predeterminados :

  • Todos : este administrador de conflictos resuelve los conflictos seleccionando 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 gestor de conflictos selecciona solo la revisión ''más reciente'', la última se define mediante una cadena de comparación de revisiones.
  • último compatible : este administrador de conflictos selecciona la última versión en los conflictos que pueden dar como resultado 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);
  • estricto : este administrador de conflictos lanza una excepción (es decir, causa un error de compilación) cada vez que se encuentra un conflicto.

Si es necesario, puede proporcionar su propio administrador de conflictos .


"¿Es esta la situación actual?"

No con OSGi. Es posible que desee ver los paquetes 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 cargador de clases no jerárquico, de modo que puede cargar múltiples versiones de log4j, y diferentes paquetes pueden especificar y vincularse a esas diferentes versiones.

Javaworld tiene una muy buena introducción here .


"¿Es este el estado actual de las 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 actualizadas. Por ejemplo, podría elegir usar Red Hat Enterprise Linux. Si se ajusta exactamente a lo que admiten (y nada más), entonces la configuración se resuelve.

Sin embargo, las probabilidades son buenas de que ninguna configuración empaquetada cumpla con sus requisitos.