Messagebodyprovidernotfoundexception While Running Jar from Command Line

MessageBodyProviderNotFoundException while running jar from command line

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.

MessageBodyWriter not found Error when running from fatjar

Try loading the dependencies you need explicitly on code and not relying on auto discovery, registering them on Jersey.

The fat jar plug-in tries to analyze your code and see what's being used and what's not, so it can exclude references not used and make a smaller jar. The problem with auto discovery is that since there are no explicit references to some classes, the shade plug-in can just think their aren't being used so they can be removed.

I think there's a way to force them to be included on the plug-in configuration, but I don't remember that configuration from memory, you would have to search for it in the docs.

MessageBodyWriter not found error for Jersy / Jetty server

As mentioned in my answer in MessageBodyProviderNotFoundException while running jar from command line, there is a "services file", namely org.glassfish.jersey.internal.spi.AutoDiscoverable, which is included in many jars. The purpose of this file is to allow Jersey (and other third party) jars to provide some auto-registration for features included in those jars. This includes registration of the JacksonFeature, which registers the JSON providers that handle (de)serialization.

The problem with creating fat (uber) jars is that there can only be one of those files (you can't have more than one file of the same name). So with all the jars included in the fat jar, only one service file will be included.

With Maven, we would use the maven-shade-plugin, which has transformers that allow for transforming parts of the build. The shade plugin has a ServicesResourceTransformer which takes care of concatenating the contents of the service files into one service file. The plugin you are using for Gradle, Shadow, has the same facility. You configure it by calling mergeServiceFiles() in the configuration. I don't really work with Gradle, but also stated in this issue, the recommended configuration for handling both the service files transformation and the main class manifest transformation is the following

shadowJar {
mergeServiceFiles()
manifest {
attributes 'Main-Class': 'com.my.Application'
}
}

So I am assuming, with the above configuration, you can also remove

jar { manifest { attributes 'Main-Class': "${mainClassName}" } }

as the Shadow plugin will take care of building the manifest. Again, I don't really use Gradle, so this is just an assumption; but it sounds right.

How to turn my Jersey REST API into an executable JAR?

Follow these steps to create a standalone application with Jersey and Tomcat:

Adding Maven dependencies

Add the following dependencies and properties to your pom.xml:

<properties>
<tomcat.version>8.5.23</tomcat.version>
<jersey.version>2.26</jersey.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>${jersey.version}</version>
</dependency>
</dependencies>

Creating JAX-RS resource classes

Define your JAX-RS resource class(es). The following is just an example:

@Path("hello")
public class HelloWorldResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
public Response helloWorld() {
return Response.ok("Hello World").build();
}
}

Creating a Jersey configuration class

Create a class to configure your Jersey application:

public class JerseyConfiguration extends ResourceConfig {

public JerseyConfiguration() {
packages("com.example");
}
}

Creating a launcher class for Tomcat

Create a class to launch Tomcat and deployment your application:

public class Launcher {

private static final String JERSEY_SERVLET_NAME = "jersey-container-servlet";

public static void main(String[] args) throws Exception {
new Launcher().start();
}

void start() throws Exception {

String port = System.getenv("PORT");
if (port == null || port.isEmpty()) {
port = "8080";
}

String contextPath = "";
String appBase = ".";

Tomcat tomcat = new Tomcat();
tomcat.setPort(Integer.valueOf(port));
tomcat.getHost().setAppBase(appBase);

Context context = tomcat.addContext(contextPath, appBase);
Tomcat.addServlet(context, JERSEY_SERVLET_NAME,
new ServletContainer(new JerseyConfiguration()));
context.addServletMappingDecoded("/api/*", JERSEY_SERVLET_NAME);

tomcat.start();
tomcat.getServer().await();
}
}

Adding Maven plugin for creating an executable JAR

Finally add the Maven Shade plugin to create an executable JAR, where the mainClass attribute references the launch class:

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<configuration>
<finalName>tomcat-embedded-example-${project.version}</finalName>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.Launcher</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Compiling and running the application

To compile and run the application, follow these steps:

  • Open a command line window or terminal.
  • Navigate to the root directory of the project, where the pom.xml resides.
  • Compile the project: mvn clean compile.
  • Package the application: mvn package.
  • Look in the target directory. You should see a file with the following or a similar name: tomcat-embedded-example-1.0-SNAPSHOT.jar.
  • Change into the target directory.
  • Execute the JAR: java -jar tomcat-embedded-example-1.0-SNAPSHOT.jar.
  • The application should be available at http://localhost:8080/api/hello.

See more

  • How to deploy a JAX-RS application on a Java SE environment?


Related Topics



Leave a reply



Submit