Qu'est-ce qu'une dépendance Java «ombrée»?

76

Développeur JVM ici. Dernièrement, j'ai vu des plaisanteries sur les forums de discussion IRC et même dans mon propre bureau à propos de bibliothèques dites " ombrées " Java. Le contexte de l'utilisation sera quelque chose comme:

" Tel et tel fournit un client" ombré "pour XYZ. "

L’exemple parfait de ce problème Jira pour HBase est le suivant : " Publier un artefact client avec des dépendances ombrées "

Je demande donc: Qu'est-ce qu'un JAR ombré , qu'est-ce que cela signifie d'être "ombré"?

smeeb
la source

Réponses:

88

Le masquage des dépendances consiste à inclure et à renommer des dépendances (ce qui permet de déplacer les classes et de réécrire le code et les ressources affectés) afin de créer une copie privée que vous regroupez avec votre propre code .

Le concept est généralement associé aux uber-jars (aka jar fat ).

Il y a une certaine confusion à propos du terme , en raison de maven shade plugin, qui sous ce nom fait 2 choses (en citant leur propre page):

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 des dépendances.

La partie ombrage est donc optionnelle: le plugin permet d’inclure des dépendances dans votre jar (fat jar), et éventuellement de renommer des dépendances (nuance) .

Ajout d' une autre source :

Ombrer une bibliothèque, c'est prendre les fichiers de contenu de cette bibliothèque, les mettre dans votre propre jarre et changer leur paquet . Cela diffère du packaging qui consiste simplement à expédier les fichiers de bibliothèques dans votre propre fichier JAR sans les déplacer dans un autre package.

Techniquement, les dépendances sont ombrées. Mais il est courant de se référer à une dépendance fat-jar-with-shaded en tant que "jar shaded", et si ce jar est un client d'un autre système, il peut être appelé "client ombré".

Voici le titre du numéro de Jira pour HBase que vous avez lié à votre question:

Publier un artefact client avec des dépendances ombrées

Donc, dans ce post, j'essaie de présenter les 2 concepts sans les mélanger.

Le bon

Uber-jars est souvent utilisé pour expédier une application sous forme de fichier unique (facilitant son déploiement et son exécution). Ils peuvent également être utilisés pour expédier des bibliothèques avec certaines (ou toutes) leurs dépendances ombrées , afin d'éviter les conflits lorsqu'elles sont utilisées par d'autres applications (qui peuvent utiliser différentes versions de ces bibliothèques).

Il y a plusieurs façons de construire uber-jars, mais maven-shade-pluginva encore plus loin avec sa fonctionnalité de relocalisation de classe :

Si le fichier JAR uber est réutilisé en tant que dépendance d'un autre projet, l'inclusion directe de classes provenant des dépendances de l'artefact dans le fichier JAR uber peut entraîner des conflits de chargement de classe en raison de classes dupliquées sur le chemin de classe. Pour résoudre ce problème, il est possible de déplacer les classes incluses dans l'artefact ombré afin de créer une copie privée de leur code secondaire.

(Note historique: Les liens de jar Jar proposaient cette fonctionnalité de relocalisation auparavant)

Vous pouvez donc utiliser les dépendances de bibliothèque comme un détail d'implémentation , sauf si vous exposez des classes de ces bibliothèques dans votre API.

Supposons que j'ai un projet, ACME Quantanizer ™, qui fournit de la DecayingSyncQuantanizerclasse et dépend d'Apache commons-rng (car bien sûr, pour quantifier correctement, vous avez besoin d'un XorShift1024Star, duh).

Si j'utilise le plugin shade maven pour produire un uber-jar, et que je regarde à l'intérieur, je vois les fichiers de classe suivants:

com/acme/DecayingSyncQuantanizer.class
org/apache/commons/rng/RandomProviderState.class
org/apache/commons/rng/RestorableUniformRandomProvider.class
...
org/apache/commons/rng/core/source64/XorShift1024Star.class
org/apache/commons/rng/core/util/NumberFactory.class

Maintenant, si j'utilise la fonctionnalité de déplacement de classe:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.0.0</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <relocations>
          <relocation>
            <pattern>org.apache.commons</pattern>
            <shadedPattern>com.acme.shaded.apachecommons</shadedPattern>
          </relocation>
        </relocations>
      </configuration>
    </execution>
  </executions>
</plugin>

Le contenu de uber-jar ressemble à ceci:

com/acme/DecayingSyncQuantanizer.class
com/acme/shaded/apachecommons/rng/RandomProviderState.class
com/acme/shaded/apachecommons/rng/RestorableUniformRandomProvider.class
...
com/acme/shaded/apachecommons/rng/core/source64/XorShift1024Star.class
com/acme/shaded/apachecommons/rng/core/util/NumberFactory.class

Il ne s’agit pas seulement de renommer des fichiers, il réécrit du bytecode qui fait référence aux classes déplacées (ainsi, mes propres classes et classes communes sont toutes transformées).

De plus, le plug-in Shade générera également un nouveau POM ( dependency-reduced-pom.xml) dans lequel les dépendances ombrées sont supprimées de la <dependencies>section. Cela aide à utiliser le fichier jar ombré en tant que dépendance pour un autre projet. Vous pouvez donc publier ce fichier au lieu de celui de base, ou des deux (à l'aide d'un qualificateur pour le fichier ombré).

Cela peut donc être très utile ...

Le mauvais

... mais cela pose également un certain nombre de problèmes. L'agrégation de toutes les dépendances dans un seul "espace de noms" dans le fichier jar peut devenir compliquée et nécessiter de l'ombrage et des problèmes de ressources.

Par exemple: comment traiter les fichiers de ressources qui incluent des noms de classe ou de package? Les fichiers de ressources tels que les descripteurs de fournisseur de services dans lesquels tous vivent META-INF/services?

Le plugin shade propose des transformateurs de ressources pouvant vous aider:

L'agrégation des classes / ressources de plusieurs artefacts dans un même JAR est simple tant qu'il n'y a pas de chevauchement. Sinon, une certaine logique pour fusionner les ressources de plusieurs JAR est requise. C’est là que les transformateurs de ressources entrent en jeu.

Mais cela reste compliqué et les problèmes sont presque impossibles à anticiper (vous les découvrez souvent à la dure dans la production). Voyez pourquoi nous avons arrêté de construire des pots de graisse .

Dans l’ensemble, le déploiement d’un pot de graisse en tant qu’application / service autonome est encore très courant; vous devez simplement être au courant des pièges et, pour certains d’entre eux, vous pourriez avoir besoin d’ ombrage ou d’autres astuces.

Le moche

Il y a beaucoup plus de problèmes difficiles (débogage, testabilité, compatibilité avec les chargeurs de classe OSGi & exotiques ...).

Mais plus important encore, lorsque vous produisez une bibliothèque, les divers problèmes que vous pensiez pouvoir contrôler devenaient maintenant infiniment plus compliqués, car votre fichier jar serait utilisé dans de nombreux contextes différents (contrairement à un fichier jar gros que vous déploieriez en tant qu'application / service autonome). dans un environnement contrôlé).

Par exemple, ElasticSearch ombrageait certaines dépendances dans les bocaux expédiés, mais ils ont décidé d'arrêter de le faire :

Avant la version 2.0, Elasticsearch était fourni sous forme de fichier JAR avec certaines dépendances communes (mais pas toutes) ombrées et conditionnées dans le même artefact. Cela a aidé les utilisateurs de Java intégrant Elasticsearch dans leurs propres applications à éviter les conflits de versions de modules tels que Guava, Joda, Jackson, etc. Bien entendu, il existait toujours une liste d'autres dépendances non ombrées telles que Lucene qui pouvaient toujours provoquer des conflits.
Malheureusement, l’ombrage est un processus complexe et sujet aux erreurs qui résout les problèmes de certaines personnes tout en créant des problèmes pour d’autres. Les ombrages empêchent les développeurs et les auteurs de plug-ins d'écrire et de déboguer le code correctement, car les packages sont renommés lors de la construction. Enfin, nous avions l'habitude de tester Elasticsearch sans ombrage, puis d'expédier le pot ombré, et nous n'aimons rien envoyer que nous ne testions pas.
Nous avons décidé d'expédier Elasticsearch sans ombrage à partir de la version 2.0.

S'il vous plaît noter qu'ils font aussi référence à des dépendances ombrées , pas jar ombré

Hugues M.
la source
1
Merci d'avoir pris le temps de l'expliquer. La documentation officielle du plugin maven shade est totalement inadéquate et ne discute pas de cela, ni même la peine de définir "uber jar". Cette documentation est obtuse et inutile. Votre rédaction est utile.
Cheeso
Excellente explication, je pense que cela devrait être inclus dans la documentation officielle
Adelin
7

Permettez-moi de répondre à la question à l'aide du logiciel en charge de la création de bocaux ombragés ... du moins lorsque vous utilisez Maven.

Extrait de la page d'accueil Apache Maven Shade Plugin :

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 des dépendances.

Un fichier jar ombré, dit uber-jar, ou jar gros, contient par défaut toutes les dépendances nécessaires à l'exécution de l'application Java, de sorte qu'aucune dépendance supplémentaire ne soit requise dans le chemin d'accès aux classes. Vous n'avez besoin que de la version Java appropriée pour exécuter votre application. Un jarre ombragé aidera à éviter les problèmes de déploiement / classpath, mais il sera beaucoup plus grand que le jar de l’application originale et ne vous aidera pas à éviter l’enfer de jar.

Jesko R.
la source
1
Peur que cette réponse est incomplète: elle explique ce que sont les pots de graisse / uber, mais n'explique pas la partie ombrée . Et oui, l'ombrage est censé aider à 100% avec "l'enfer de jar" (ce qui rend la dernière partie de cette réponse incorrecte). Donc, c'est utile à un certain niveau, mais ajoute à la confusion: - /
Hugues M.
1
@ HuguesMoreau Je ne suis peut-être pas complet à 100% dans ma réponse, mais cela apportait tout de même le point que je voulais faire valoir. Merci d'avoir apporté la partie manquante sur la table. Shading n'évitera pas l'enfer, c'est ce que je voulais dire et écrire, mais cela vous donnera quelques outils à portée de main vous permettant de résoudre certains de ses problèmes, mais ce n'est pas automatique. Ce qui fait la dernière partie si lu et interprété comme je le pensais, du moins ok. :)
Jesko R.