web-dev-qa-db-fra.com

A quoi sert le plugin maven-shade-plugin, et pourquoi voudriez-vous déplacer les paquets Java?

J'ai trouvé le plugin Maven-shade-utilisé dans le fichier pom.xml de quelqu'un. Je n'avais jamais utilisé maven-shade-plugin auparavant (et je suis un Maven n00b), alors j'ai essayé de comprendre la raison de son utilisation et son utilisation.

J'ai regardé Maven docs , mais je ne comprends pas cette affirmation:

"Ce plugin offre la possibilité de conditionner l'artefact dans un uber-jar, y compris ses dépendances, et de masquer, c'est-à-dire renommer, les packages de certaines de ces dépendances." La documentation sur la page ne semble pas très conviviale pour les débutants.

Qu'est-ce qu'un "uber jar"? Pourquoi quelqu'un voudrait en faire un? Quel est l'intérêt de renommer les packages des dépendances? J'ai essayé de passer en revue les exemples de la page Apache de maven-shade-plugin, tels que "Sélection du contenu pour Uber Jar", mais je ne comprends toujours pas ce qui est accompli avec "l'ombrage".

Tous les exemples d’illustrations/cas d’utilisation (avec une explication de la raison pour laquelle un ombrage était nécessaire dans ce cas - quel problème est-il résolu-il résoudre) seraient-ils appréciés? Enfin, quand devrais-je utiliser le plugin maven-shade-plugin?

254
nonbeing

Uber JAR, en bref, est un JAR contenant tout.

Normalement, dans Maven, nous nous appuyons sur la gestion de la dépendance. Un artefact ne contient que ses classes/ressources. Maven sera responsable de rechercher tous les artefacts (JAR, etc.) du projet, en fonction de leur date de construction.

Un uber-jar est quelque chose qui prend toutes les dépendances, extrait le contenu des dépendances et les met avec les classes/ressources du projet lui-même, dans un grand JAR. En ayant un tel uber-jar, il est facile à exécuter, car vous n’avez besoin que d’un seul gros JAR au lieu de tonnes de petits JAR pour exécuter votre application. Cela facilite également la distribution dans certains cas.

Juste une note de côté. Évitez d’utiliser uber-jar en tant que dépendance Maven, car elle ruine la fonctionnalité de résolution des dépendances de Maven. Normalement, nous créons uber-jar uniquement pour l'artefact final pour le déploiement réel ou pour la distribution manuelle, mais pas pour la mise en référentiel Maven.


Mise à jour: Je viens de découvrir que je n'ai pas répondu à une partie de la question: "Quel est l'intérêt de renommer les packages des dépendances?". Voici quelques brèves mises à jour et espérons pouvoir aider les personnes ayant des questions similaires.

Créer un uber-jar pour faciliter le déploiement est un cas d'utilisation du plug-in shade. Il existe également d'autres cas d'utilisation courants qui impliquent de renommer un paquet.

Par exemple, je développe la bibliothèque Foo, qui dépend d’une version spécifique (par exemple, 1.0) de la bibliothèque Bar. En supposant que je ne puisse pas utiliser une autre version de Bar lib (en raison de la modification de l'API ou d'autres problèmes techniques, etc.). Si je déclare simplement la dépendance de Bar:1.0 en tant que dépendance de Foo dans Maven, il est possible de tomber dans un problème: Un projet Qux dépend de Foo, ainsi que Bar:2.0 (et il ne peut pas utiliser Bar:1.0 parce que Qux doit utiliser une nouvelle fonctionnalité dans Bar:2.0). Voici le dilemme: Qux doit-il utiliser Bar:1.0 (le code de Qux ne fonctionnera pas) ou Bar:2.0 (le code de Foo ne fonctionnera pas)?

Pour résoudre ce problème, le développeur de Foo peut choisir d’utiliser le plug-in shade pour renommer son utilisation de Bar, de sorte que toutes les classes de Bar:1.0 jar soient incorporées à Foo. jar et le package des classes Bar incorporées passe de com.bar à com.foo.bar. Ce faisant, Qux peut dépendre de Bar:2.0 en toute sécurité, car Foo ne dépend plus de Bar et utilise sa propre copie de "modifié" Bar situé dans un autre package.

303
Adrian Shum

Je me demandais récemment pourquoi elasticsearch ombrage et déplace quelques-unes (mais pas toutes) de ses dépendances. Voici une explication du responsable du projet, @ kimchy:

La partie ombrée est intentionnelle, les bibliothèques ombrées que nous utilisons dans elasticsearch font partie intégrante de elasticsearch. La version utilisée est étroitement liée à ce que expose elasticsearch et à la façon dont il utilise la bibliothèque, en se basant sur le fonctionnement interne de la bibliothèque (et qui change entre les versions), netty et goyave sont d’excellents exemples.

En fait, je n'ai aucun problème à fournir plusieurs pots d'elasticsearch, un avec Lucene non ombré et un avec Lucene ombré. Je ne sais pas comment le faire avec Maven cependant. Je ne souhaite pas fournir une version qui ne nuise pas netty/jackson par exemple, en raison de la profonde intimité avec laquelle elasticsearch est associée (par exemple, en utilisant la mise à jour à venir avec une version précédente de netty sauf pour la version actuelle utilisez réellement plus de mémoire que d’en utiliser beaucoup moins).

- https://github.com/elasticsearch/elasticsearch/issues/2091#issuecomment-7156766

Et un autre ici de drewr :

L'ombrage est important pour maintenir nos dépendances (notamment netty, lucene, goyave) près de notre code afin de pouvoir résoudre un problème même si le fournisseur en amont est à la traîne. Il est possible que nous distribuions des versions modulaires du code, ce qui pourrait vous aider avec votre problème particulier (n ° 2091 par exemple), mais nous ne pouvons pas simplement supprimer les dépendances ombrées pour le moment. Vous pouvez créer une version locale d'ES adaptée à vos besoins jusqu'à ce qu'une meilleure solution soit trouvée.

- https://github.com/elasticsearch/elasticsearch/pull/3244#issuecomment-20125452

Donc, c'est un cas d'utilisation. En guise d'exemple illustratif, voici comment maven-shade-plugin est utilisé dans le fichier pom.xml (v0.90.5) d'elasticsearch. Les lignes artifactSet::include lui indiquent les dépendances à intégrer dans le fichier JAR uber (elles sont fondamentalement décompressées et reconditionnées en même temps que les classes propres d’elasticsearch lors de la production du fichier java cible elasticsearch.) Un fichier JAR n’est qu’un fichier Zip contenant les classes, ressources, etc. du programme, ainsi que des métadonnées (vous pouvez en extraire un pour voir comment il est assemblé.)

Les lignes relocations::relocation sont similaires, sauf qu'elles appliquent également les substitutions spécifiées aux classes de dépendance - dans ce cas, les plaçant sous org.elasticsearch.common.

Enfin, la section filters exclut certains éléments du fichier JAR cible qui ne devrait pas figurer - tels que les métadonnées JAR, les fichiers de construction ant, les fichiers texte, etc., empaquetés avec certaines dépendances, mais qui n'appartiennent pas dans un JAR uber.

<plugins>
    <plugin>
        <groupId>org.Apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.1</version>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <minimizeJar>true</minimizeJar>
            <artifactSet>
                <includes>
                    <include>com.google.guava:guava</include>
                    <include>net.sf.trove4j:trove4j</include>
                    <include>org.mvel:mvel2</include>
                    <include>com.fasterxml.jackson.core:jackson-core</include>
                    <include>com.fasterxml.jackson.dataformat:jackson-dataformat-smile</include>
                    <include>com.fasterxml.jackson.dataformat:jackson-dataformat-yaml</include>
                    <include>joda-time:joda-time</include>
                    <include>io.netty:netty</include>
                    <include>com.ning:compress-lzf</include>
                </includes>
            </artifactSet>
            <relocations>
                <relocation>
                    <pattern>com.google.common</pattern>
                    <shadedPattern>org.elasticsearch.common</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>gnu.trove</pattern>
                    <shadedPattern>org.elasticsearch.common.trove</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>jsr166y</pattern>
                    <shadedPattern>org.elasticsearch.common.util.concurrent.jsr166y</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>jsr166e</pattern>
                    <shadedPattern>org.elasticsearch.common.util.concurrent.jsr166e</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.mvel2</pattern>
                    <shadedPattern>org.elasticsearch.common.mvel2</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>com.fasterxml.jackson</pattern>
                    <shadedPattern>org.elasticsearch.common.jackson</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.joda</pattern>
                    <shadedPattern>org.elasticsearch.common.joda</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.jboss.netty</pattern>
                    <shadedPattern>org.elasticsearch.common.netty</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>com.ning.compress</pattern>
                    <shadedPattern>org.elasticsearch.common.compress</shadedPattern>
                </relocation>
            </relocations>
            <filters>
                <filter>
                    <artifact>*:*</artifact>
                    <excludes>
                        <exclude>META-INF/license/**</exclude>
                        <exclude>META-INF/*</exclude>
                        <exclude>META-INF/maven/**</exclude>
                        <exclude>LICENSE</exclude>
                        <exclude>NOTICE</exclude>
                        <exclude>/*.txt</exclude>
                        <exclude>build.properties</exclude>
                    </excludes>
                </filter>
            </filters>
        </configuration>
    </plugin>
</plugins>
58
Tom

Petit avertissement

Bien que cela ne décrive pas pourquoi on aimerait utiliser le plugin maven-shade-plugin (puisque la réponse sélectionnée le décrit assez bien), je voudrais noter que j'ai eu des problèmes avec ce plugin. Cela a changé le fichier JAR (depuis que ce qu'il fait) et a provoqué une régression dans mon logiciel.

Donc, au lieu d’utiliser ceci (ou le plugin maven-jarjar), j’ai utilisé le binaire de JarJar qui semble fonctionner sans problème.

Je poste ici ma solution car il m'a fallu du temps pour trouver une solution décente.


Téléchargez le fichier JAR de JarJar

Vous pouvez télécharger le fichier JAR à partir d'ici: https://code.google.com/p/jarjar/ Dans le menu de gauche, vous trouverez un lien pour le télécharger.


Comment utiliser JarJar pour déplacer des classes d'un JAR d'un paquet à un autre

Dans cet exemple, nous allons remplacer le paquet "com.fasterxml.jackson" par "io.kuku.dependencies.com.fasterxml.jackson". - Le fichier JAR source s'appelle "jackson-databind-2.6.4.jar" et le nouveau fichier JAR modifié (cible) s'appelle "kuku-jackson-databind-2.6.4.jar". - Le fichier JAR "jarjar" est en version 1.4

  1. Créez un fichier "rules.txt". Le contenu du fichier doit être (regardez la période précédant le caractère "@"): règle com.fasterxml.jackson. ** io.kuku.dependencies.com.fasterxml.jackson. @ 1

  2. Exécutez la commande suivante: Java -jar jarjar-1.4.jar processus rules.txt jackson-databind-2.6.4.jar kuku-jackson-databind-2.6.4.jar


Installation des fichiers JAR modifiés dans le référentiel local

Dans ce cas, j'installe 3 fichiers situés dans le dossier "c:\my-jars \".

mvn install: fichier d'installation -Dfile = C:\my-jars\annotations-kuku-jackson-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = annotations-kuku-jackson -Dversion = 2.6.4 - Dpackaging = pot

mvn install: install-file -Dfile = C:\my-jars\kuku-jackson-core-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-core-Dversion = 2.6.4 - Dpackaging = pot

mvn install: install-file -Dfile = C:\my-jars\kuku-jackson-databind-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-annotations -Dversion = 2.6.4 - Dpackaging = pot


Utilisation des fichiers JAR modifiés dans le pom du projet

Dans cet exemple, il s'agit de l'élément "dependencies" dans les projets pom:

<dependencies>
    <!-- ================================================== -->
    <!-- kuku JARs -->
    <!-- ================================================== -->
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-annotations</artifactId>
        <version>2.6.4</version>
    </dependency>
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-core</artifactId>
        <version>2.6.4</version>
    </dependency>
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-databind</artifactId>
        <version>2.6.4</version>
    </dependency>
</dependencies>
2
nadavy