| ------ |
| Resource Transformers |
| ------ |
| Mauro Talevi |
| ------ |
| 2008-07-21 |
| ------ |
| |
| ~~ 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. |
| |
| Resource Transformers |
| |
| Aggregating classes/resources from several artifacts into one uber JAR is straight forward as long as there is no |
| overlap. Otherwise, some kind of logic to merge resources from several JARs is required. This is where resource |
| transformers kick in. |
| |
| |
| *-----------------------------------------+------------------------------------------+ |
| | {{ApacheLicenseResourceTransformer}} | Prevents license duplication | |
| *-----------------------------------------+------------------------------------------+ |
| | {{ApacheNoticeResourceTransformer}} | Prepares merged NOTICE | |
| *-----------------------------------------+------------------------------------------+ |
| | {{AppendingTransformer}} | Adds content to a resource | |
| *-----------------------------------------+------------------------------------------+ |
| | {{ComponentsXmlResourceTransformer}} | Aggregates Plexus <<<components.xml>>> | |
| *-----------------------------------------+------------------------------------------+ |
| | {{DontIncludeResourceTransformer}} | Prevents inclusion of matching resources | |
| *-----------------------------------------+------------------------------------------+ |
| | {{GroovyResourceTransformer}} | Merges Apache Groovy extends modules | |
| *-----------------------------------------+------------------------------------------+ |
| | {{IncludeResourceTransformer}} | Adds files from the project | |
| *-----------------------------------------+------------------------------------------+ |
| | {{ManifestResourceTransformer}} | Sets entries in the <<<MANIFEST>>> | |
| *-----------------------------------------+------------------------------------------+ |
| | {{MicroprofileConfigTransformer}} | Merges conflicting Microprofile Config properties based on an ordinal | |
| *-----------------------------------------+------------------------------------------+ |
| | {{OpenWebBeansPropertiesTransformer}} | Merges Apache OpenWebBeans configuration files | |
| *-----------------------------------------+------------------------------------------+ |
| | {{PluginXmlResourceTransformer}} | Aggregates Mavens <<<plugin.xml>>> | |
| *-----------------------------------------+------------------------------------------+ |
| | {{PropertiesTransformer}} | Merges properties files owning an ordinal to solve conflicts | |
| *-----------------------------------------+------------------------------------------+ |
| | {{ResourceBundleAppendingTransformer}} | Merges ResourceBundles | |
| *-----------------------------------------+------------------------------------------+ |
| | {{ServicesResourceTransformer}} | Relocated class names in <<<META-INF/services>>> resources and merges them. | |
| *-----------------------------------------+------------------------------------------+ |
| | {{XmlAppendingTransformer}} | Adds XML content to an XML resource | |
| *-----------------------------------------+------------------------------------------+ |
| Transformers in <<<org.apache.maven.plugins.shade.resource>>> |
| |
| * Merging Plexus Component Descriptors with the {ComponentsXmlResourceTransformer} |
| |
| JARs for components targeting the Plexus IoC container contain a <<<META-INF/plexus/components.xml>>> entry that |
| declares the component and its requirements. If the uber JAR aggregates multiple Plexus components, a |
| <<<ComponentsXmlResourceTransformer>>> needs to be used to merge the XML descriptors: |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer"/> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| Since plugin version 1.3, this resource transformer will also update the descriptor to account for relocation of |
| component interfaces/implementations (if any). |
| |
| * Relocate classes of the Maven Plugin Descriptor with the {PluginXmlResourceTransformer} |
| |
| With {{{http://maven.apache.org/plugin-tools/index.html}Plugin Tools 3.0}} annotations have been introduced. Now references |
| to classes are no longer classnames as String, but the actual Class reference. When you wanted to relocate classes, you had to |
| maintain the <<<META-INF/maven/plugin.xml>>> by hand, but now this can be done with the <<<PluginXmlResourceTransformer>>> |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.PluginXmlResourceTransformer"/> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| * Concatenating Service Entries with the {ServicesResourceTransformer} |
| |
| JAR files providing implementations of some interfaces often ship with a <<<META-INF/services/>>> directory that |
| maps interfaces to their implementation classes for lookup by the service locator. |
| To relocate the class names of these implementation classes, and to |
| merge multiple implementations of the same interface into one service entry, |
| the <<<ServicesResourceTransformer>>> can be used: |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| * Merging Content of Specific Files with {AppendingTransformer}, XmlAppendingTransformer and ResourceBundleAppendingTransformer |
| |
| Some jars contain additional resources (such as properties files) that have the same file name. To avoid overwriting, you can |
| opt to merge them by appending their content into one file. One good example for this is when aggregating both the spring-context |
| and plexus-spring jars. Both of them have the <<<META-INF/spring.handlers>>> file which is used by Spring to handle XML schema |
| namespaces. You can merge the contents of all the files with that specific name using the <<<AppendingTransformer>>> as shown below: |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> |
| <resource>META-INF/spring.handlers</resource> |
| </transformer> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> |
| <resource>META-INF/spring.schemas</resource> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| For XML files, you can use the <<<{XmlAppendingTransformer}>>> instead: |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer"> |
| <resource>META-INF/magic.xml</resource> |
| <!-- Add this to enable loading of DTDs |
| <ignoreDtd>false</ignoreDtd> |
| --> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| Since plugin version 1.3.1, the <<<XmlAppendingTransformer>>> will by default not load DTDs, thereby avoiding network |
| access. The potential downside of this mode is that external entities cannot be resolved which could fail the |
| transformation, e.g. when using the Crimson XML parser as used in some JRE 1.4. If the transformed resource uses |
| external entities, DTD resolution can either be turned back on or a plugin dependency on <<<xerces:xercesImpl:2.9.1>>> |
| is added to the POM. |
| |
| For ResourceBundles properties files, you can use the <<<{ResourceBundleAppendingTransformer}>>> instead, which will respect all available Locales as well: |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.ResourceBundleAppendingTransformer"> |
| <!-- the base name of the resource bundle, a fully qualified class name --> |
| <basename>path/to/Messages</basename> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| * Excluding Resources with the {DontIncludeResourceTransformer} |
| |
| The <<<DontIncludeResourceTransformer>>> allows resources to be excluded when |
| their name ends in a given value. |
| |
| For example, the following sample excludes all resources ending in <<<.txt>>>. |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer"> |
| <resource>.txt</resource> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| Since maven-shade-plugin-3.0 it is also possible to give a list of resources which should not be included, like: |
| |
| +----- |
| <transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer"> |
| <resources> |
| <resource>.txt</resource> |
| <resource>READ.me</resource> |
| </resources> |
| </transformer> |
| +----- |
| |
| |
| |
| * Adding New Resources with the {IncludeResourceTransformer} |
| |
| The <<<IncludeResourceTransformer>>> allows project files to be included in |
| the package under a given name. |
| |
| For example, the following sample includes <<<README.txt>>> in the package |
| as <<<README>>> in the <<<META-INF>>> directory. |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer"> |
| <resource>META-INF/README</resource> |
| <file>README.txt</file> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| |
| * Setting Manifest Entries with the {ManifestResourceTransformer} |
| |
| The <<<ManifestResourceTransformer>>> allows existing entries in the <<<MANIFEST>>> |
| to be replaced and new entries added. |
| |
| For example, the following sample sets |
| |
| * the <<<Main-Class>>> entry to the value of the <<<app.main.class>>> property, |
| |
| * the <<<X-Compile-Source-JDK>>> entry to the value of the <<<maven.compile.source>>> property and |
| |
| * the <<<X-Compile-Target-JDK>>> entry to the value of the <<<maven.compile.target>>> property. |
| |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> |
| <manifestEntries> |
| <Main-Class>${app.main.class}</Main-Class> |
| <X-Compile-Source-JDK>${maven.compile.source}</X-Compile-Source-JDK> |
| <X-Compile-Target-JDK>${maven.compile.target}</X-Compile-Target-JDK> |
| </manifestEntries> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| |
| * Licensing |
| |
| ** Preventing License Duplication with the {ApacheLicenseResourceTransformer} |
| |
| Some open source producers |
| (including the {{{http://www.apache.org} Apache Software Foundation}}) |
| include a copy of their license in the META-INF directory. These are conventionally named |
| either <<<LICENSE>>> or <<<LICENSE.txt>>>. When merging these dependencies, adding these |
| resources may cause confusion. |
| The <<<ApacheLicenseResourceTransformer>>> ensures that duplicate licenses (named according |
| to this convention) are not merged. |
| |
| For example, the following prevents the license from a <<<commons-collections>>> dependency |
| being merged in |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer"> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| ** Aggregating Notices with the {ApacheNoticeResourceTransformer} |
| |
| Some licenses |
| (including the {{{http://www.apache.org/licenses/LICENSE-2.0.html} Apache License, Version 2}}) |
| require that notices are preserved by downstream distributors. <<<ApacheNoticeResourceTransformer>>> |
| automates the assembly of an appropriate <<<NOTICE>>>. |
| |
| For example, to simply merge in dependent notices: |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer"> |
| <addHeader>false</addHeader> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| ** Aggregating Apache Groovy extension modules descriptors with the {GroovyResourceTransformer} |
| |
| The Apache Groovy language provides extension modules located at |
| <<<META-INF/services/org.codehaus.groovy.runtime.ExtensionModule>>>, these modules use the property file format. |
| <<<GroovyResourceTransformer>>> automates the assembly of Groovy extension modules <<<NOTICE>>>. |
| |
| For example, to simply merge the extension modules of several jars: |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.GroovyResourceTransformer"> |
| <extModuleName>the-aggregated-module</extModuleName> |
| <extModuleVersion>1.0.0</extModuleVersion> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| * Merging properties files with {PropertiesTransformer} |
| |
| The <<<PropertiesTransformer>>> allows a set of properties files to be merged and to resolve conflicts |
| based on an ordinal giving the priority of each file. |
| An optional <<<alreadyMergedKey>>> enables to have a boolean flag in the file which, if set to true, |
| request to use the file as it as the result of the merge. If two files are considered complete in the |
| merge process then the shade will fail. |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.properties.PropertiesTransformer"> |
| <!-- required configuration --> |
| <resource>configuration/application.properties</resource> |
| <ordinalKey>ordinal</ordinalKey> |
| <!-- optional configuration --> |
| <alreadyMergedKey>already_merged</alreadyMergedKey> |
| <defaultOrdinal>0</defaultOrdinal> |
| <reverseOrder>false</reverseOrder> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| * Merging Apache OpenWebBeans configuration with {OpenWebBeansPropertiesTransformer} |
| |
| <<<OpenWebBeansPropertiesTransformer>>> preconfigure a <<<PropertiesTransformer>>> |
| for Apache OpenWebBeans configuration files. |
| |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.properties.OpenWebBeansPropertiesTransformer" /> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |
| * Merging Microprofile Config properties with {MicroprofileConfigTransformer} |
| |
| <<<MicroprofileConfigTransformer>>> preconfigure a <<<PropertiesTransformer>>> |
| for Microprofile Config. The only required configuration is the ordinal. |
| The <<<alreadyMergedKey>>> is supported but is not defined by the specification. |
| |
| |
| +----- |
| <project> |
| ... |
| <build> |
| <plugins> |
| <plugin> |
| <groupId>org.apache.maven.plugins</groupId> |
| <artifactId>maven-shade-plugin</artifactId> |
| <version>${project.version}</version> |
| <executions> |
| <execution> |
| <goals> |
| <goal>shade</goal> |
| </goals> |
| <configuration> |
| <transformers> |
| <transformer implementation="org.apache.maven.plugins.shade.resource.properties.MicroprofileConfigTransformer"> |
| <resource>configuration/app.properties</resource> |
| </transformer> |
| </transformers> |
| </configuration> |
| </execution> |
| </executions> |
| </plugin> |
| </plugins> |
| </build> |
| ... |
| </project> |
| +----- |
| |