¿Para qué se utiliza el complemento maven-shade y por qué querría reubicar los paquetes Java?


He encontrado el maven-shade-plugin que se utiliza en el pom de alguien.XML. Nunca he usado maven-shade-plugin antes (y soy un Maven n00b) así que traté de entender la razón para usar esto y lo que hace.

Miré los documentos Maven , sin embargo no puedo entender esta afirmación:

"Este plugin proporciona la capacidad de empaquetar el artefacto en un uber-jar, incluyendo sus dependencias y sombrear - es decir, renombrar - los paquetes de algunas de las dependencias." El la documentación en la página no parece muy amigable para los novatos.

¿Qué es un "uber jar"?"¿Por qué alguien querría hacer uno? ¿Cuál es el punto de cambiar el nombre de los paquetes de las dependencias? Traté de ir a través de los ejemplos en la página de apache maven-shade-plugin como "Selección de contenidos para Uber Jar," pero todavía no puedo entender lo que se está logrando con "sombreado."

Cualquier punteros a ejemplos ilustrativos / casos de uso (con una explicación de por qué se requería sombreado en este caso - ¿qué problema está resolviendo) sería apreciado. Por último, ¿cuándo debo usar el maven-shade-plugin?

Author: Lii, 2012-11-29

3 answers

Uber JAR, en resumen, es un FRASCO que contiene todo.

Normalmente en Maven, dependemos de la gestión de dependencias. Un artefacto contiene solo las clases / recursos de sí mismo. Maven será responsable de averiguar todos los artefactos (tarros, etc.) que el proyecto dependiendo de cuándo se construye el proyecto.

Un uber-jar es algo que toma todas las dependencias, y extrae el contenido de las dependencias y las pone con las clases/recursos del proyecto en sí, en un gran JAR. Al tener tal uber-jar, es fácil de ejecutar, porque solo necesitará un gran FRASCO en lugar de toneladas de frascos pequeños para ejecutar su aplicación. También facilita la distribución en algunos casos.

Solo una nota al margen. Evite usar uber-jar como dependencia de Maven, ya que está arruinando la función de resolución de dependencias de Maven. Normalmente creamos uber-jar solo para el artefacto final para el despliegue real o para la distribución manual, pero no para ponerlo en el repositorio Maven.


Actualización: Tengo acabo de descubrir que no he respondido a una parte de la pregunta: "¿Cuál es el punto de cambiar el nombre de los paquetes de las dependencias?". Aquí hay algunas actualizaciones breves y esperamos que ayuden a las personas que tienen preguntas similares.

Crear uber-jar para facilitar la implementación es un caso de uso del complemento shade. También hay otros casos de uso comunes que involucran el cambio de nombre de paquetes.

Por ejemplo, estoy desarrollando la biblioteca Foo, que depende de una versión específica (por ejemplo, 1.0) de la biblioteca Bar. Asumiendo que no puedo hacer uso de otra versión de Bar lib (debido a cambios en la API, u otros problemas técnicos, etc.). Si simplemente declaro Bar:1.0 como la dependencia de Foo en Maven, es posible caer en un problema: Un proyecto Qux depende de Foo, y también Bar:2.0 (y no puede usar Bar:1.0 porque Qux necesita usar una nueva característica en Bar:2.0). Aquí está el dilema: si Qux utilizar Bar:1.0 (que Qux's código no funcionará) o Bar:2.0 (que Foo's código no funcionará)?

Con el fin de resolver este problema, el desarrollador de Foo puede elegir usar el complemento shade para cambiar el nombre de su uso de Bar, de modo que todas las clases en Bar:1.0 jar estén incrustadas en Foo jar, y el paquete de las clases embebidas Bar se cambie de com.bar a com.foo.bar. Al hacerlo, Qux puede depender con seguridad de Bar:2.0 porque ahora Foo ya no depende de Bar, y está utilizando su propia copia de "altered" Bar ubicada en otro paquete.

 269
Author: Adrian Shum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-12 02:20:20

Me preguntaba recientemente por qué elasticsearch matiza y reubica algunas (pero no todas) de sus dependencias. Aquí hay una explicación del responsable del proyecto, @kimchy:

La parte de sombreado es intencional, las bibliotecas sombreadas que usamos en elasticsearch son para toda la parte de intención y propósito de elasticsearch, la versión utilizada está estrechamente vinculada a lo que elasticsearch expone y cómo usa la biblioteca en función de los aspectos internos de cómo funciona la biblioteca (y eso cambia entre versiones), netty y guava son grandes ejemplos.

Por cierto, no tengo ningún problema con proporcionar varios frascos de elasticsearch, uno con lucene no sombreado y otro con Lucene sombreado. Sin embargo, no estoy seguro de cómo hacerlo con Maven. No quiero proporcionar una versión que no sombree netty/jackson, por ejemplo, debido al uso íntimo profundo que elasticsearch tiene con ellos (por ejemplo, usando la próxima mejora de buffering con cualquier versión anterior de netty, excepto por el actual, realmente usará más memoria en comparación con el uso considerablemente menor).

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

Y otro aquí de drewr:

El sombreado es importante para mantener nuestras dependencias (notablemente netty, lucene, guava) cerca de nuestro código para que podamos solucionar un problema incluso si el proveedor de origen se queda atrás. Es posible que lo hagamos versiones modularizadas distribuidas del código, lo que ayudaría con su problema particular (#2091 por ejemplo), pero no podemos simplemente eliminar las dependencias sombreadas en este momento. Puede crear una versión local de ES para sus propósitos hasta que haya una mejor solución.

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

Entonces, ese es un caso de uso. En cuanto a un ejemplo ilustrativo, a continuación se muestra cómo maven-shade-plugin se utiliza en el pom de Elasticsearch.xml (v0.90.5). Las líneas artifactSet::include le indican qué dependencias introducir en el JAR de uber (básicamente, se descomprimen y se vuelven a empaquetar junto con las propias clases de elasticsearch cuando se produce el jar de elasticsearch de destino. (En caso de que no lo supiera ya, un archivo JAR es solo un archivo ZIP que contiene las clases, recursos, etc. del programa. y algunos metadatos. Puedes extraer uno para ver cómo se junta.)

Las líneas relocations::relocation son similares, excepto que en cada en caso de que también apliquen las sustituciones especificadas a las clases de la dependencia-en este caso, poniéndolas bajo org.elasticsearch.common.

Finalmente, la sección filters excluye algunas cosas del JAR de destino que no deberían estar allí, como metadatos del JAR, archivos de compilación ant, archivos de texto, etc. que están empaquetados con algunas dependencias, pero que no pertenecen a un uber JAR.

<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>
 55
Author: Tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:07

Pequeña advertencia

Aunque eso no describe por qué a uno le gustaría usar el complemento maven-shade (ya que la respuesta seleccionada lo describe bastante bien), me gustaría señalar que tuve problemas con él. Cambió el TARRO (ya que lo que está haciendo) y causó regresión en mi software.

Entonces, en lugar de usar esto (o el plugin maven-jarjar), he usado el binario de JarJar que parece funcionar sin problemas.

Estoy publicando aquí mi solución ya que me llevó algún tiempo para encontrar una solución decente.


Archivo JAR de Downlaod JarJar

Puede descargar el frasco desde aquí: https://code.google.com/p/jarjar / En el menú de la izquierda tienes un enlace para descargarlo.


Cómo usar JarJar para reubicar clases de un FRASCO de un paquete a otro

En este ejemplo cambiaremos el paquete de "com.fasterxml.jackson " to "io. kuku.dependencies.com.fasterxml.jackson". - El FRASCO fuente se llama "jackson-databind-2.6.4.jar " y el nuevo JAR modificado (objetivo) se llama "kuku-jackson-databind-2.6.4.frasco". - El archivo JAR "jarjar" está en la versión 1.4

  1. Crear una " reglas.txt archivo". El contenido del archivo debe ser (ver el punto antes del carácter'@'): regla com.fasterxml.jackson.** io.kuku.dependencies.com.fasterxml.jackson.@1

  2. Ejecute el siguiente comando: java-jar jarjar-1.4.reglas del proceso jar.txt jackson-databind-2.6.4.frasco kuku-jackson-databind-2.6.4.jar


Instalando los JARs modificados en el repositorio local

En este caso estoy instalando 3 archivos ubicados en "c:\my-jars \" carpeta.

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

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

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=jar


Usando los frascos modificados en el pom del proyecto

En este ejemplo, este es el elemento "dependencies" en el proyecto 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>
 1
Author: nadavy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-13 13:55:56