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
How to Find Repeated Characters with a Regex in Java
Java Bigdecimal: Round to the Nearest Whole Value
Java Serialization - Java.Io.Invalidclassexception Local Class Incompatible
How to Create a New Packaging Type for Maven
Java Import VS Code Performance
Apache Commons Equals/Hashcode Builder
Comparing the Values of Two Generic Numbers
Parsing Nested JSON Data Using Gson
How to Use Sqoop in Java Program
Merging Two JSON Documents Using Jackson
What's the Use of Session.Flush() in Hibernate
Sonar Violation: Security - Array Is Stored Directly
Eclipse Reading Stdin (System.In) from a File
Java Date Cut Off Time Information
Getting Enum Associated with Int Value
Setting Outer Variable from Anonymous Inner Class
Java Command Line with External .Jar
Org.Postgresql.Util.Psqlexception: Fatal: Sorry, Too Many Clients Already