Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MessageBodyProviderNotFoundException while running jar from command line

I am using the Java Jersey framework(with Maven), and use IntelliJ as my IDE. I have encountered this runtime exception that ONLY happens when I try to run the code from the command line (using maven to compile and then java -jar ) but NOT when running within IntelliJ, which is strange.

I have some Java code that will try to make an HTTP GET on some remote URL and try to read the returned JSON into some Lombok POJO :

String targetUrl = "some valid URL";

WebTarget webTarget = client.target(targetUrl);

Response response = webTarget.request(MediaType.APPLICATION_JSON_TYPE).get();

ParseResponse parseResponse = response.readEntity(ParseResponse.class);

I am not sure why, but when it hits that last line that does the "readEntity()" method, I will get the error below:

org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=text/json; charset=utf-8

This is strange, because I definitely have the jersey-media-json-jackson dependency specified in my pom.xml :

<dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.23</version>
</dependency>

and this is my POJO class that I was trying to readEntity() into :

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ParseResponse {

   @JsonProperty("id")
   private Integer id;

   ...Other params...
}

And like I mentioned before, it is strange that this only happens when I try to run on the command line like this but there is no error when running in IntelliJ:

mvn clean package
java -jar target/NameOfJar.jar

Did I miss something obvious here? I have looked at other people with similar issues like this online but haven't found a solution.

Thanks IS

like image 401
user1805458 Avatar asked Jun 09 '16 20:06

user1805458


1 Answers

If you look inside the jersey-media-json-jackson jar you should see a file

META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable

The contents of this file should be a single fully qualified name of a class that implements the name of the file, namely

org.glassfish.jersey.jackson.internal.JacksonAutoDiscoverable

This file is used by Jersey auto-discoverable mechanism to automatically register features without us having to explicitly register them. Briefly, how it works, is that all Jersey modules/jars that have components that should be automatically registered, should have the above named file located in the jar, with the contents being the name(s) of the auto-discoverable component. Jersey will then use the Service Loader pattern to load the classes named in the file, and register them.

The problem this causes when creating uber jars is that you can only have one copy of a file, you can't have duplicates. So what if we have multiple jars with the above file? Well only one of those files will be included in the uber jar. Which one? Who knows, but there is only one lucky winner. So for the rest of the jars, their auto-discover mechanism never kicks in. This is the case with your Jackson feature, where the auto-discoverable registers the JacksonFeature. You can try to explicitly register with your application, and you should see that it now works.

But what about other jars/modules that may have this file? It's for this reason that when creating uber jars, you should use the maven-shade-plugin. What this plugin allows you to do, is combine the contents of the files so that all the discoverables get included into that one single file. Below is an example usage

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.3</version>
    <configuration>
        <createDependencyReducedPom>true</createDependencyReducedPom>
        <filters>
            <filter>
                <artifact>*:*</artifact>
                <excludes>
                    <exclude>META-INF/*.SF</exclude>
                    <exclude>META-INF/*.DSA</exclude>
                    <exclude>META-INF/*.RSA</exclude>
                </excludes>
            </filter>
        </filters>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.example.YourApp</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

This example was actually taken from Dropwizard's Getting Started. You can check it out for further explanation. The main part of concern the ServicesResorceTransformer, which is what concatenates the services files.

like image 79
Paul Samsotha Avatar answered Oct 31 '22 16:10

Paul Samsotha