DEV Community

Jefferson Quesado
Jefferson Quesado

Posted on • Originally published at computaria.gitlab.io

Criando projeto Maven com profiles distintos de dependências

No trabalho tivemos um problema com a GraalVM, e devido a isso essa issue foi criada.

Eu falei com o autor e pensei "será que não seria melhor criar um projeto maven com isso bonitinho? Subir um repo, MCVE, que você controla através de profiles as dependências?"

E cá está o repo! github.com/jeffque/graalvm-regression

Pois bem, aproveitar que vou fazer isso e compartilhar um pouco o processo.

Iniciando um projeto maven

Para começar, eu gosto de iniciar o projeto maven colocar o mvnw (maven wrapper). Eu normalmente começo copiando o mvnw de um outro repositório meu conhecido, mas posso fazer isso de modo mais canônico. Como por exemplo, seguindo esse tutorial do Baeldung:

$ mvn -N wrapper:wrapper
Enter fullscreen mode Exit fullscreen mode

Ficou assim o diretório:

.
├── .mvn
│   └── wrapper
│       └── maven-wrapper.properties
├── mvnw
└── mvnw.cmd
Enter fullscreen mode Exit fullscreen mode

Qual a versão do maven que ele usa? Podemos perguntar diretamente ao mvwn ou então consultar em .mvn/wrapper/maven-wrapper.properties. No caso, o conteúdo do arquivo é:

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
Enter fullscreen mode Exit fullscreen mode

Muito bem, maven na versão 3.9.9, uma delícia.

Criando o projeto base

Existem diversas maneiras de iniciar um projeto maven. Inclusive a de você
o pom.xml totalmente do zero. Mas... um modo mais canônico seria pedir um
arquétipo, que nem eu fiz em "Hello, World!" em GWT usando arquétipo do TBroyer.

Então, vamos pedir o arquétipo vazio? Normalmente eu peço o maven-archetype-quickstart. Tem uma lista não exaustiva de arquétipos aqui.

./mvnw archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5
Enter fullscreen mode Exit fullscreen mode

Após baixar as dependências necessárias para rodar o arquétipo ele me pergunta:

  • Define value for property 'groupId'
    • com.jeffque
  • Define value for property 'artifactId'
    • graalvm-24-1-2-regression
  • Define value for property 'version' 1.0-SNPASHOT
    • <enter> (o que é equivalente a usar o default, que no caso é 1.0-SNAPSHOT)
  • Define value for property 'package' com.jeffque:
    • <enter> (o que é equivalente a usar o default, que no caso é com.jeffque)

Após responder, ele pede para confirmar:

Define value for property 'package' com.jeffque: 
Confirm properties configuration:
javaCompilerVersion: 17
junitVersion: 5.11.0
groupId: com.jeffque
artifactId: graalvm-24-1-2-regression
version: 1.0-SNAPSHOT
package: com.jeffque
 Y:
Enter fullscreen mode Exit fullscreen mode

Confimei e... puff!

.
├── .mvn
│   └── wrapper
│       └── maven-wrapper.properties
├── graalvm-24-1-2-regression
│   ├── .mvn
│   │   ├── jvm.config
│   │   └── maven.config
│   ├── pom.xml
│   └── src
│       ├── main
│       │   └── java
│       │       └── com
│       │           └── jeffque
│       │               └── App.java
│       └── test
│           └── java
│               └── com
│                   └── jeffque
│                       └── AppTest.java
├── mvnw
└── mvnw.cmd
Enter fullscreen mode Exit fullscreen mode

Gerei no canto errado? Ok, só mover uma pasta pra lá

\leftarrow

.

cd graalvm-24-1-2-regression
mv pom.xml src ../
mv .mvn/* ../.mvn
rmdir .mvn
cd ..
rmdir graalvm-24-1-2-regression
Enter fullscreen mode Exit fullscreen mode

Ok, ajeitado:

.
├── .mvn
│   ├── jvm.config
│   ├── maven.config
│   └── wrapper
│       └── maven-wrapper.properties
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── com
    │           └── jeffque
    │               └── App.java
    └── test
        └── java
            └── com
                └── jeffque
                    └── AppTest.java
Enter fullscreen mode Exit fullscreen mode

Adicionando comando de execução

Eu quero facilitar a vida de quem vai testar usando Maven. Para tal, vou configurar o plugin exec. O plugin é esse: exec-maven-plugin.

Na linha de comando:

> ./mvnw exec:java -Dexec.mainClass="com.jeffque.App"
[INFO] Scanning for projects...
[INFO] 
[INFO] ---------------< com.jeffque:graalvm-24-1-2-regression >----------------
[INFO] Building graalvm-24-1-2-regression 1.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- exec:3.5.0:java (default-cli) @ graalvm-24-1-2-regression ---
Hello World!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.383 s
[INFO] Finished at: 2025-01-31T08:02:13-03:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode Exit fullscreen mode

Beleza, vamos por no pom? Em pluginManagement a gente indica commo o plugin vai ser executado ao ser chamado, não necessariamente ele será chamado. Então, se eu adicionar o exec-maven-plugin lá, o maven não tentará fazer o fetch desse plugin a priori. Já em build.plugins... aí a gente está indicando que o plugin será de fato executado.

Aí vem a questão: quando usar algo em build.plugins? Quando precisamos que o plugin seja executado e não tem implícito em algum lugar isso. Como por exemplo, chamar o retrolambda para permitir usar a sintaxe de lambdas e conseguir dar um target para java 7.

Ok, vamos adicionar as configurações do plugin em pluginManagement? Primeiramente, vamos colocar o plugin lá:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>3.5.0</version>
</plugin>
Enter fullscreen mode Exit fullscreen mode

Ok, prendemos a versão. Agora, vamos para o como vai ser chamado. No caso, quando for chamado o mojo exec:java: exec identifica esse plugin e java o goal específico:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>3.5.0</version>
    <executions>
        <execution>
            <goals>
                <goal>java</goal>
            </goals>
        </execution>
    </executions>
</plugin>
Enter fullscreen mode Exit fullscreen mode

Ok, agora, vamos por a configuração da classe principal:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>3.5.0</version>
    <executions>...</executions>
    <configuration>
        <mainClass>com.jeffque.App</mainClass>
    </configuration>
</plugin>
Enter fullscreen mode Exit fullscreen mode

Plugin configurado! Será?

> ./mvnw exec:java
[INFO] Scanning for projects...
[INFO] 
[INFO] ---------------< com.jeffque:graalvm-24-1-2-regression >----------------
[INFO] Building graalvm-24-1-2-regression 1.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- exec:3.5.0:java (default-cli) @ graalvm-24-1-2-regression ---
Hello World!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.341 s
[INFO] Finished at: 2025-01-31T08:28:48-03:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode Exit fullscreen mode

Adaptando ao problema da issue

Vamos colocar as coisas da issue do GitHub. Primeiramente, para a versão problemática do GraalVM. Vamos alterar a main para refletir como está na issue.

Adicionar as dependências e alterar a main foi uma non-issue, bem direto ao ponto. Para testar, coloquei as dependências para a versão do GraalVM com problema e mandei executar:

> ./mvnw compile exec:java
...
org.graalvm.polyglot.PolyglotException: TypeError: k.equals is not a function
    at <js>.:=> (Unnamed:6)
    at com.oracle.truffle.polyglot.PolyglotFunctionProxyHandler.invoke (PolyglotFunctionProxyHandler.java:151)
...
Enter fullscreen mode Exit fullscreen mode

Perfeito! Tal qual aparece na issue!

Profiles

Vou criar perfis distintos. E neles vou colocar as dependências. Você pode ler mais na documentação oficial.

Vou criar dois perfis:

  • graalvm-24, apresenta o problema
  • graalvm-20, não apresenta o problema
<profiles>
    <profile>
        <id>graalvm-24</id>
    </profile>
    <profile>
        <id>graalvm-20</id>
    </profile>
</profiles>
Enter fullscreen mode Exit fullscreen mode

Ok. Agora, vou deixar o perfil graalvm-24 ativo por default. Se ninguém falar nada, ele que será invocado:

<profile>
    <id>graalvm-24</id>
    <activation>
        <activeByDefault>true</activeByDefault>
    </activation>
</profile>
Enter fullscreen mode Exit fullscreen mode

Como invocar esse perfil? Passando a opção -P na linha de comando!

Por exemplo:

./mvnw exec:java -Pgraalvm-24
Enter fullscreen mode Exit fullscreen mode

Estou explicitando que quero o perfil graalvm-24. De modo semelhante:

./mvnw exec:java -Pgraalvm-20
Enter fullscreen mode Exit fullscreen mode

Para o perfil graalvm-20. Eu poderia passar múltiplos perfis também:

./mvnw exec:java -Pgraalvm-20,jeff,marmota
Enter fullscreen mode Exit fullscreen mode

Isso ativa os perfis graalvm-20, jeff e marmota. E no caso o activateByDefault, funciona como? Bem, se você não falar nada...

./mvnw exec:java
# note que não tem -P
Enter fullscreen mode Exit fullscreen mode

Aí só ativa o que tá cadastrado pra rodar por padrão.

Muito bem, agora eu coloco as dependências:

<profiles>
    <profile>
        <id>graalvm-24</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <dependencies>
            <dependency>
                <groupId>org.graalvm.polyglot</groupId>
                <artifactId>polyglot</artifactId>
                <version>24.1.2</version>
            </dependency>
            <dependency>
                <groupId>org.graalvm.polyglot</groupId>
                <artifactId>js-community</artifactId>
                <version>24.1.2</version>
                <type>pom</type>
                <scope>runtime</scope>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>graalvm-20</id>
        <dependencies>
            <dependency>
                <groupId>org.graalvm.sdk</groupId>
                <artifactId>graal-sdk</artifactId>
                <version>20.1.0</version>
            </dependency>
            <dependency>
                <groupId>org.graalvm.js</groupId>
                <artifactId>js</artifactId>
                <version>20.1.0</version>
            </dependency>
        </dependencies>
    </profile>
</profiles>
Enter fullscreen mode Exit fullscreen mode

E pronto, agora tenho dois perfis distintos! Note que o graalvm foi removido do project.dependencies, pois essas dependências sem perfil afetam a todos.

Para testar:

> ./mvnw exec:java
...
org.graalvm.polyglot.PolyglotException: TypeError: k.equals is not a function
    at <js>.:=> (Unnamed:6)
...

> ./mvnw exec:java -P graalvm-20
...
[INFO] --- exec:3.5.0:java (default-cli) @ graalvm-24-1-2-regression ---
Optional[true]
[INFO] ------------------------------------------------------------------------
...
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
loboweissmann profile image
Henrique Lobo Weissmann (Kico)

É ótimo ver novos textos escritos sobre Maven. O pessoal usa hoje em dia sem nem pensar direito como funciona.

Muito bom!

Collapse
 
jeffque profile image
Jefferson Quesado

Eu criei pq na minha época de criar coisa maven eu não tive acesso a nenhum material sobre profiles.

Espero que esse artigo encontre mais novatos no mundo maven e que eles entendam que tipos de problema o profile pode resolver

Collapse
 
jessilyneh profile image
Jessilyneh

Nossa, bem interessante esse case que resolveram! É bem normal eu ter problemas de ter que me virar com versões depreciadas de bibliotecas por compatibilidade, curti demais!