Pergunta

Temos um grande sistema Java (> 500.000 loc) que depende de 40-50 pacotes de OSS.O sistema é construído com ANT e o gerenciamento de dependência é tratado manualmente no momento.Estou investigando Ivy e/ou Maven para automatizar dependências.Analisamos o MAVEN como um sistema de automação de compilação no ano passado e o rejeitamos porque exigiria totalmente reestruturar nosso sistema para combinar com a arquitetura do MAVEN.Agora estou procurando automatizar apenas as tarefas de gerenciamento de dependência.

Fiz algumas experiências com Ivy, mas tive problemas.Por exemplo, quando eu especificar o ActiveMQ como uma dependência e digo a Ivy para usar os POMs no repositório maven para especificação de dependência, a Ivy recupera vários pacotes (Jetty, Derby e Geronimo, por exemplo) que eu sei que não são necessários para apenas Use ActiveMq.

Se eu definir usepoms = "false" em ivysettings.xml, ele busca apenas ActiveMq.jar, mas isso parece derrotar o objetivo da hera e relegá-lo a um simples Fetcher com especificações de dependência fabricadas manualmente.

Há uma questão maior aqui, o que costumava ser chamado de "Dll ​​Hell" no Windows.Em alguns casos, duas dependências diretas de primeiro nível apontarão para diferentes versões da mesma dependência transitiva (por exemplo, log4j.jar).Apenas um log4j.jar pode estar no caminho de classe, portanto, a resolução de dependência envolve determinar manualmente qual versão é compatível com todos os seus clientes em nosso sistema.

Eu acho que tudo se resume à qualidade da especificação de dependência de cada pacote (o POM).No caso do ActiveMQ, não há declarações de escopo; portanto, qualquer referência ao ActiveMQ baixará todas as suas dependências, a menos que excluamos manualmente os que sabemos que não queremos.

No caso do log4j, a resolução automática de dependências requereria que todos os clientes do log4j (outros pacotes que dependem do log4j) validar contra todas as versões anteriores do log4j e fornecer um intervalo (ou lista) de versões log4j compatíveis no POM.Isso também é provavelmente muito a pedir.

É este o estado atual das coisas ou estou faltando alguma coisa?

Foi útil?

Solução

Você está absolutamente certo em dizer isso

Acho que tudo se resume à qualidade da especificação de dependência de cada pacote (o POM).

A única coisa que gostaria de acrescentar é ver o POM, ou qualquer outra forma de metadados, como ponto de partida.É bastante útil que, por ex.O ActiveMQ fornece todas as dependências para você, mas cabe a você escolher se ele realmente se adapta ao seu projeto.

Afinal, mesmo levando em conta a versão log4j, você teria uma dependência externa para escolher a versão ou escolher a versão que você sabe que funciona para você?


Quanto a como você pode escolher adaptar as dependências, aqui está o que você pode fazer com o Ivy:

Pacotes desnecessários

Ivy recupera vários pacotes (Jetty, Derby e Geronimo, por exemplo) que sei que não são necessários apenas para usar o ActiveMQ.

Isso geralmente acontece devido à baixa modularidade do aplicativo. Alguma parte do aplicativo precisa do Jetty, por exemplo, mas você acaba com essa dependência transitiva mesmo se não o usar.

Você provavelmente quer dar uma olhada no mecanismo de exclusão de hera:

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

Versões de dependência

Apenas um log4j.jar pode estar no caminho de classe, portanto, a resolução de dependências envolve determinar manualmente qual versão é compatível com todos os seus clientes em nosso sistema.

Talvez eu esteja interpretando mal isso, mas não há manual elemento na resolução de conflitos de Ivy.Existe uma lista de gerenciadores de conflitos padrão:

  • todos:este gerenciador de conflitos resolve conflitos selecionando todas as revisões.Também chamado de NoConflictManager, ele remove qualquer módulo.
  • última hora:este gerenciador de conflitos seleciona apenas a revisão 'mais recente', sendo a mais recente definida como a mais recente no tempo.Observe que o cálculo mais recente é caro, portanto, prefira a revisão mais recente, se puder.
  • última revisão:este gerenciador de conflitos seleciona apenas a revisão 'mais recente', sendo a mais recente definida por uma comparação de strings de revisões.
  • mais recente compatível:este gerenciador de conflitos seleciona a versão mais recente nos conflitos, o que pode resultar em um conjunto compatível de dependências.Isto significa que no final este gestor de conflitos não permite qualquer conflito (como o gestor de conflitos estrito), excepto que segue uma estratégia de melhor esforço para tentar encontrar um conjunto de módulos compatíveis (de acordo com as restrições de versão);
  • estrito:este gerenciador de conflitos lança uma exceção (ou seja,causa uma falha de compilação) sempre que um conflito for encontrado.

Se necessário, você pode forneça seu próprio gerenciador de conflitos.

Outras dicas

É basicamente isso.O sistema de dependência maven (que Ivy segue mais ou menos) deixa para os projetos individuais fazerem um bom trabalho adicionando os metadados necessários para suas dependências.A maioria não.

Se você seguir esse caminho, espere gastar tempo configurando exclusões.

Para os postadores recomendando o OSGi, o OP disse que não está disposto a re-arquitetar seu sistema de construção para Maven, eu não acho que ele iria querer re-arquitetar seu aplicativo para ser compatível com OSGi.Além disso, muitos projetos OSS compatíveis com OSGi (e não há tantos quanto você esperaria) têm metadados tão ruins ou piores do que no Maven

Das dependências listadas, as seguintes são definidas como optional no activemq-core pom (veja também o seção relevante do livro Maven).

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

Não vi uma dependência direta do Jetty, portanto ele pode ser incluído transitivamente em uma das dependências opcionais.

No Maven, as dependências opcionais são tratadas automaticamente.Essencialmente, qualquer dependência declarada opcional deve ser declarada novamente em seu pom para ser usada.Da documentação vinculada acima:

Dependências opcionais são usadas quando não é realmente possível (por qualquer motivo) dividir um projeto em submódulos.A ideia é que algumas das dependências sejam usadas apenas para determinados recursos do projeto e não serão necessárias se esse recurso não for usado.Idealmente, tal recurso seria dividido em um submódulo que dependesse do projeto de funcionalidade principal... esse novo subprojeto teria apenas dependências não opcionais, já que você precisaria de todas elas se decidisse usar a funcionalidade do subprojeto.

Entretanto, como o projeto não pode ser dividido (novamente, por qualquer motivo), essas dependências são declaradas opcionais.Se um usuário quiser usar funcionalidade relacionada a uma dependência opcional, ele terá que declarar novamente essa dependência opcional em seu próprio projeto.Esta não é a maneira mais clara de lidar com esta situação, mas, novamente, tanto as dependências opcionais quanto as exclusões de dependência são soluções provisórias.

Não tenho certeza se você pode configurar o Ivy para ignorar as dependências opcionais, mas você pode configurá-lo para excluir dependências.Por exemplo:

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

Isso não é totalmente satisfatório, eu sei.Pode ser que o Ivy suporte dependências opcionais (darei uma olhada mais detalhada e atualizarei se encontrar alguma coisa), mas o mecanismo de exclusões pelo menos permite que você as gerencie.


Em relação à última parte da sua pergunta.O Maven resolverá as versões de dependência do log4j e, se as versões forem compatíveis, selecionará automaticamente a 'mais próxima' das versões listadas.

De Introdução ao Mecanismo de Dependência:

  • Mediação de dependência – determina qual versão de uma dependência será usada quando múltiplas versões de um artefato forem encontradas.Atualmente, o Maven 2.0 suporta apenas o uso da "definição mais próxima", o que significa que ele usará a versão da dependência mais próxima do seu projeto na árvore de dependências.Você sempre pode garantir uma versão declarando-a explicitamente no POM do seu projeto.Observe que se duas versões de dependências estão na mesma profundidade na árvore de dependências, até o Maven 2.0.8 não era definido qual delas venceria, mas desde o Maven 2.0.9 é a ordem na declaração que conta:a primeira declaração vence.

    • "definição mais próxima" significa que a versão usada será a mais próxima do seu projeto na árvore de dependências, por exemplo.se as dependências para A, B e C forem definidas como A -> B -> C -> D 2.0 e A -> E -> D 1.0, então D 1.0 será usado ao construir A porque o caminho de A a D através E é mais curto.Você poderia adicionar explicitamente uma dependência ao D 2.0 em A para forçar o uso do D 2.0

Onde as versões não são compatíveis, você tem um problema maior do que a resolução de dependências.Acredito que Ivy opera segundo um modelo semelhante, mas não sou especialista.

Penso que esta é realmente a situação actual. OSGi e o novo sistema de empacotamento proposto para java 1.7 (a discussão sobre isso já chegou a uma conclusão?) são tentativas de corrigir pelo menos o problema de dependência de diferentes versões de uma biblioteca, mas não acho eles poderão resolver seu problema agora mesmo.

Atualmente estou usando o Ivy para gerenciar mais de 120 OSS e bibliotecas proprietárias para diversos projetos (alguns independentes, alguns dependentes).Em 2005 (quando Ivy ainda era da Jayasoft) decidi (ou tive que fazer) escrever os arquivos ivy.xml para cada pacote integrado.

A maior vantagem é que tenho controle total sobre as diversas configurações.Isso pode parecer um exagero para alguns, mas nosso sistema de compilação tem funcionado de forma confiável há mais de 4 anos e adicionar uma nova biblioteca normalmente leva 5 minutos.

“É este o estado atual das coisas?”

Não com OSGi.Você pode querer dar uma olhada nos contêineres e pacotes OSGi.Um pacote é como um arquivo jar, mas suporta metadados detalhando sua versão e as versões de pacotes relacionados que ele requer (colocando atributos no arquivo META-INF).Portanto, seu pacote log4j indicará sua versão, e os pacotes dependentes detalharão quais versões do log4j eles exigem.

Além disso, um mecanismo de carregador de classe não hierárquico é suportado, de modo que você pode carregar diversas versões do log4j e diferentes pacotes configuráveis ​​podem especificar e vincular-se a essas diferentes versões.

Javaworld tem uma introdução muito boa aqui.

Existe toda a ideia de injeção de dependência - o que invariavelmente levaria à necessidade de reestruturação do programa.Tenho ouvido alguns rumores sobre o GUICE ser bom nesse aspecto.Na perspectiva de implantação, tive um sucesso razoável ao implantar apenas o .jar que construímos com a dependência .jars sendo obtida de projetos originais via jnlp.O sistema de compilação por trás disso envolveu o rastreamento manual de novas versões de dependências e atualização no sistema de compilação.

"É este o estado atual das coisas"

Sim.

Esta é a compensação do código aberto.

Uma estrutura de código fechado (ou seja, .Net) resolverá tudo isso para você.

Uma solução de código aberto significa que você precisa resolvê-la (e resolvê-la) o tempo todo.

Você pode encontrar algumas configurações pré-construídas e pagar alguém para mantê-las atualizadas.Por exemplo, você pode optar por usar o Red Hat Enterprise Linux.Se você se limitar exatamente ao que eles suportam (e nada mais), a configuração estará resolvida.

As probabilidades são boas, entretanto, de que nenhuma configuração empacotada atenda aos seus requisitos.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top