javaeclipsemavenuberjar

java runtime exception: "could not find writer for content type" when building uberjar and zip package


When using the maven assembly plugin to build an uberjar and then package it into a zip file, I encounter a runtime failure:

java.lang.RuntimeException: could not find writer for content-type text/xml type: java.lang.String

This failure does not occur when I run my project within eclipse, or when I create and executable .jar using the eclipse Export -> Runnable Jar File so I suspect there is something wrong with the way I'm using maven creating the uberjar.

How do I fix this problem?


Solution

  • Turns out, the root of my problem was a conflict with the javax.ws.rs.ext.Providers file that occurs when the maven assembly plugin creates the jar. (This file can be found in the uberjar within META-INF -> services -> javax.ws.rs.ext.Providers)

    The Providers file contains a list of available provider classes. Within the dependencies of my project this file exists in more than one place, and the different copies contain different provider lists. The maven assembly plugin simply chooses one version to include in the jar, and so at runtime the required "writer" class cannot be found: This class is not listed in the Providers file within the jar.

    I used the maven shade plugin to overcome this problem. The shade plugin contains a facility to selectively merge duplicate files contained within the dependency tree. Within pom.xml:

      <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
          <resource>META-INF/services/javax.ws.rs.ext.Providers</resource>
      </transformer>
    

    Tells maven to merge by appending any duplicates of javax.ws.rs.ext.Providers.

    Also, by setting the maven shade plugin to execute during the package phase of my build, and then the maven assembly plugin to execute at the install phase, I was able to create an executable uberjar, then package that uberjar within a .zip file, all with a simple mvn clean install invocation.

    Here's what my pom.xml looks like:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
       <modelVersion>4.0.0</modelVersion>
    
       <parent>
          ...
       </parent>
    
       <groupId>com.foo.bar</groupId>
       <artifactId>my-app</artifactId>
       <packaging>jar</packaging>
       <version>2.1.0.0-SNAPSHOT</version>
       <name>My App</name>
    
       <url>http://maven.apache.org</url>
    
       <properties>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          <issues-product>MyApp</issues-product>
          <issues-component>MY-APP</issues-component>
       </properties>
    
       <dependencies>
          ...
       </dependencies>
    
       <build>
       <finalName>${project.artifactId}</finalName>
       <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
            </configuration>
        </plugin>
         <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>
                <configuration>
                <createDependencyReducedPom>false</createDependencyReducedPom>
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <mainClass>com.foo.bar.MyMainClass</mainClass>
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                            <resource>META-INF/services/javax.ws.rs.ext.Providers</resource>
                        </transformer>
                    </transformers>
                    <filters>
                        <filter>
                            <artifact>*:*</artifact>
                        </filter>
                    </filters>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.2.2</version>
            <configuration>
                <finalName>${project.artifactId}</finalName>
                <appendAssemblyId>false</appendAssemblyId>
                <descriptors>
                    <descriptor>src/assembly/my-app-assembly.xml</descriptor>
                </descriptors>
             </configuration>
             <executions>
                <execution>
                    <phase>install</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
       </plugins>
    </project>
    

    And here is my-app-assembly.xml:

    <assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/2.2.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/2.2.2 http://maven.apache.org/xsd/assembly-2.2.2.xsd">
      <id>bin</id>
      <formats>
        <format>zip</format>
      </formats>
      <includeBaseDirectory>false</includeBaseDirectory>
      <fileSets>
        <fileSet>
          <directory>${project.basedir}</directory>
          <outputDirectory/>
          <includes>
            <include>Readme.pdf</include>
            <include>config\</include>
            <include>input\</include>
            <include>output\</include>
          </includes>
        </fileSet>
        <fileSet>
          <directory>${project.build.directory}</directory>
          <outputDirectory>bin\java\</outputDirectory>
          <includes>
            <include>my-app.jar</include>
          </includes>
        </fileSet>
      </fileSets>
    </assembly>