Existe alguma maneira de excluir artefatos herdados de um POM pai?

119

Os artefatos de dependências podem ser excluídos declarando um <exclusions>elemento dentro de um <dependency>Mas, neste caso, é necessário excluir um artefato herdado de um projeto pai. Segue um trecho do POM em discussão:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>test</groupId>
  <artifactId>jruby</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
    </parent>

    <dependencies>      
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>ALL-DEPS</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</project>

baseartefato, depende javax.mail:mail-1.4.jare ALL-DEPSdepende de outra versão da mesma biblioteca. Devido ao fato mail.jarde ALL-DEPSexistir no ambiente de execução, embora não seja exportado, colide com o mail.jarque existe no pai, cujo escopo é o compile.

Uma solução poderia ser livrar o mail.jar do POM pai, mas a maioria dos projetos que herdam a base precisa disso (como é uma dependência perturbadora do log4j). Então, o que eu gostaria de fazer é simplesmente excluir a biblioteca do pai do projeto filho , pois isso poderia ser feito se basefosse uma dependência e não o pom do pai:

...
    <dependency>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
        <type>pom<type>
        <exclusions>
          <exclusion>
             <groupId>javax.mail</groupId>
             <artifactId>mail</artifactId>
          </exclusion>
        </exclusions>
    </dependency>
...
Miguel
fonte

Respostas:

49

Algumas ideias:

  1. Talvez você simplesmente não possa herdar dos pais nesse caso (e declarar uma dependência basecom a exclusão). Não é útil se você tiver muitas coisas no pom dos pais.

  2. Outra coisa a testar seria declarar o mailartefato com a versão exigida por ALL-DEPSsob o dependencyManagementpom pai para forçar a convergência (embora não tenha certeza de que isso resolverá o problema de escopo).

<dependencyManagement>
  <dependencies>
    <dependency>    
      <groupId>javax.mail</groupId>
      <artifactId>mail</artifactId>
      <version>???</version><!-- put the "right" version here -->
    </dependency>
  </dependencies>
</dependencyManagement>
  1. Ou você pode excluir a maildependência do log4j se não estiver usando os recursos que dependem dele (e é isso que eu faria):
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.15</version>
  <scope>provided</scope>
  <exclusions>
    <exclusion>
      <groupId>javax.mail</groupId>
      <artifactId>mail</artifactId>
    </exclusion>
    <exclusion>
      <groupId>javax.jms</groupId>
      <artifactId>jms</artifactId>
    </exclusion>
    <exclusion>
      <groupId>com.sun.jdmk</groupId>
      <artifactId>jmxtools</artifactId>
    </exclusion>
    <exclusion>
      <groupId>com.sun.jmx</groupId>
      <artifactId>jmxri</artifactId>
    </exclusion>
  </exclusions>
</dependency>
  1. Ou você pode reverter para a versão 1.2.14 do log4j em vez da versão herética 1.2.15 (por que eles não marcaram as dependências acima como opcionais ?!).
Pascal Thivent
fonte
Obrigado pela sua resposta. Ele contém muitas informações úteis. Em relação a 1) Como você notou, não será o ideal porque o pom pai não contém apenas dependências que serão resolvidas transitivamente se a base foi marcada como uma dependência, mas também relatórios comuns, gerenciamento de fontes e outras coisas reutilizadas entre todos os projetos da empresa. Em relação a 2) eu tentei, mas também especificando o escopo do artefato conforme fornecido, e funcionou :). No começo, eu pensei que, como a compilação tem precedência sobre a fornecida, ela não funcionará, mas felizmente eu estava errado (o POM filho substitui a configuração dos pais) #
Miguel
29

Você pode agrupar suas dependências em um projeto diferente com o empacotamento, pomconforme descrito pelas Práticas recomendadas do Sonatypes :

<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>base-dependencies</artifactId>
    <groupId>es.uniovi.innova</groupId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <dependencies>
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4</version>
        </dependency>
    </dependencies>
</project>

e referenciá-los do seu pai-pom (observe a dependência <type>pom</type>):

<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>base</artifactId>
    <groupId>es.uniovi.innova</groupId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <dependencies>
        <dependency>
            <artifactId>base-dependencies</artifactId>
            <groupId>es.uniovi.innova</groupId>
            <version>1.0.0</version>
            <type>pom</type>
        </dependency>
    </dependencies>
</project>

Seu projeto filho herda esse pom pai como antes. Mas agora, a dependência de email pode ser excluída no projeto filho dentro do dependencyManagementbloco:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>test</groupId>
    <artifactId>jruby</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <artifactId>base-dependencies</artifactId>
                <groupId>es.uniovi.innova</groupId>
                <version>1.0.0</version>
                <exclusions>
                    <exclusion>
                        <groupId>javax.mail</groupId>
                        <artifactId>mail</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
timomeinen
fonte
4
+1 para isso, embora o pom pom filho deva estar usando a seção <dependencies> em vez de <dependencyManagement>, pois o último é para gerenciar as versões de dependências de um pom pai.
Matthew Wise
1
Isso também é conhecido como o BOM, aka lista de materiais :-)
Pim Hazebroek
Isso funciona apenas com dependências transitivas? Meu pom pai contém log4j e está impedindo que o logback do meu pom funcione corretamente.
Sridhar Sarnobat
10

Não use um pom pai

Isso pode parecer extremo, mas da mesma forma que "inferno de herança" é uma razão pela qual algumas pessoas dão as costas à Programação Orientada a Objetos (ou preferem composição a herança ), removem o <parent>bloco problemático e copiam e colam o que <dependencies>você precisar (se sua equipe fornecer a você essa liberdade).

A suposição de que a divisão dos poms em pai e filho para "reutilização" e "prevenção de redunância" deve ser ignorada e você deve atender às suas necessidades imediatas primeiro (a cura é pior que a doença). Além disso, a redundância tem suas vantagens - ou seja, independência de mudanças externas (ou seja, estabilidade).

Isso é mais fácil do que parece se você gerar o pom efetivo (o eclipse fornece, mas você pode gerá-lo na linha de comando mvn help:effective).

Exemplo

Eu quero usar logbackcomo minha ligação slf4j, mas meu pom pai inclui a log4jdependência. Eu não quero ir e ter que colocar a dependência das outras crianças do log4j em seus próprios pom.xmlarquivos, para que a minha fique desobstruída.

Sridhar Sarnobat
fonte
1
Documente isso (e o procedimento que você seguiu) com muito cuidado, pois os futuros mantenedores precisam refazê-lo se precisar usar uma versão atualizada do pom pai.
Thorbjørn Ravn Andersen
Certamente você quer dizer não usar dependências no pai pom? O pom pai ainda é muito útil para gerenciar versões de dependência e plug-ins comuns. Apenas usá-lo para dependência injetar pode sair pela culatra - podemos usá-lo apenas para o must-have dependências (como um conjunto de entradas de inicialização primavera essenciais para um pai para microservices)
Amit Goldstein
Não, eu não estou dizendo para usar um pom pai leve. Outros membros da sua equipe não permitirão que você apare o pom pai, porque outros aplicativos dependem do lixo no pom pai que você não deseja.
Sridhar Sarnobat 22/03/19
8

Redefina a dependência (no pom filho) com o scopesistema apontando para um jar vazio:

<dependency>
    <groupId>dependency.coming</groupId>
    <artifactId>from.parent</artifactId>
    <version>0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/empty.jar</systemPath>
</dependency>

O jar pode conter apenas um único arquivo vazio:

touch empty.txt
jar cvf empty.txt
Bax
fonte
Obrigado pela sua resposta, no Windows 10 eu tive que correr notepad empty.classem seguida, jar cvf empty.jar empty.classpara gerar um frasco vazio.
Rov
1
Parece má prática
Piotr Żak
6

Você já tentou declarar explicitamente a versão do mail.jar que deseja? A resolução de dependência do Maven deve usar isso para resolução de dependência em todas as outras versões.

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>test</groupId>
  <artifactId>jruby</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
    </parent>
    <dependencies>          
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>VERSION-#</version>
            <scope>provided</scope>
        </dependency> 
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>ALL-DEPS</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</project>
porterhouse91
fonte
1
Sua abordagem, como a solução alternativa nº 2 de Pascal também é válida, na verdade, você também considerou que a dependência de correio deveria ser declarada como fornecida. Obrigado.
Miguel
O escopo fornecido não funcionou para mim. Eu usei o teste de escopo, por favor, verifique minha resposta. stackoverflow.com/a/55970293/4587961
Yan Khonski
3

A melhor aposta é fazer com que as dependências que você nem sempre deseja herdar sejam intransitivas.

Você pode fazer isso marcando-os no pom pai com o escopo fornecido.

Se você ainda deseja que o pai gerencie versões desses deps, pode usar a <dependencyManagement>tag para configurar as versões desejadas sem herdá-las explicitamente ou passar essa herança para os filhos.

Ajax
fonte
1

Quando você chama um pacote, mas não deseja algumas de suas dependências, pode fazer algo assim (nesse caso, não queria que o log4j antigo fosse adicionado porque precisava usar o mais novo):

<dependency>
  <groupId>package</groupId>
  <artifactId>package-pk</artifactId>
  <version>${package-pk.version}</version>

  <exclusions>
    <exclusion>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
    </exclusion>
  </exclusions>
</dependency>

<!-- LOG4J -->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.5</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.5</version>
</dependency>

Isso funciona para mim ... mas eu sou muito novo em java / maven, então talvez não seja o ideal.

bastienb1991
fonte
Bem-vindo ao Stack Overflow, e não deixe que as pessoas rudes que constantemente rejeito minhas perguntas o desanimam de postar.
Sridhar Sarnobat
1

Eu realmente precisava fazer essa coisa suja ... Aqui está como

Redefini essas dependências com escopo test. Escopoprovided não funcionou para mim.

Usamos o plugin Spring Boot para criar um jar gordo. Temos um módulo comum que define bibliotecas comuns, por exemplo, Springfox swagger-2. Meu super-serviço precisa ter um pai em comum (ele não deseja fazer isso, mas as regras da empresa são forçadas!)

Então, meu pai ou família tem pom.

<dependencyManagement>

    <!- I do not need Springfox in one child but in others ->

    <dependencies>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-bean-validators</artifactId>
            <version>${swagger.version}</version>
        </dependency>

       <!- All services need them ->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>${apache.poi.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

E meu pom super-serviço .

<name>super-service</name>
<parent>
    <groupId>com.company</groupId>
    <artifactId>common</artifactId>
    <version>1</version>
</parent>

<dependencies>

    <!- I don't need them ->

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-bean-validators</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-core</artifactId>
        <version>2.8.0</version>
        <scope>test</scope>
    </dependency>

    <!- Required dependencies ->

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
     <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
    </dependency>
</dependencies>

Esse é o tamanho do artefato final de gordura

82.3 MB (86,351,753 bytes) - redefined dependency with scope test
86.1 MB (90,335,466 bytes) - redefined dependency with scope provided
86.1 MB (90,335,489 bytes) - without exclusion

Também vale a pena mencionar esta resposta - eu queria fazê-lo, mas sou preguiçoso ... https://stackoverflow.com/a/48103554/4587961

Yan Khonski
fonte
0

Podemos adicionar o pai pom como uma dependência do tipo pom e fazer exclusão disso. Porque de qualquer forma pai pom é baixado. Isso funcionou para mim

<dependency>
  <groupId>com.abc.boot</groupId>
  <artifactId>abc-boot-starter-parent</artifactId>
  <version>2.1.5.RELEASE</version>
  <type>pom</type>
  <exclusions>
    <exclusion>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
    </exclusion>
  </exclusions>   
</dependency>
Viki Jain
fonte