I am trying to use RestEasy in a Jetty application. I am using Jackson for serializing/deserializing my POJOs. I need my serialization to be polymorphic since I have endpoints that handle a POJO type that has sub-types. I thought I could use resteasy-jackson-provider
to provide my MessageBodyWriter and MessageBodyReader implementations.
When I call an endpoint I get the following error:
java.lang.NoClassDefFoundError: org/jboss/resteasy/util/FindAnnotation
at org.jboss.resteasy.plugins.providers.jackson.ResteasyJacksonProvider.isWriteable(ResteasyJacksonProvider.java:42)
at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.resolveMessageBodyWriter(ResteasyProviderFactoryImpl.java:1322)
at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.getMessageBodyWriter(ResteasyProviderFactoryImpl.java:1293)
at org.jboss.resteasy.core.ServerResponseWriter.lambda$writeNomapResponse$2(ServerResponseWriter.java:112)
at org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:405)
at org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:232)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:97)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:70)
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:578)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:508)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:252)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:153)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:363)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:156)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:238)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:249)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:60)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:55)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:841)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:535)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:188)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:168)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:166)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:561)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:334)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:104)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:247)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:679)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:597)
at java.base/java.lang.Thread.run(Thread.java:834)
Here's my POM file:
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>com.foster.app</groupId>
<artifactId>jettyRestEasyApiService</artifactId>
<version>3.0.0</version>
<packaging>jar</packaging>
<name>Jetty-RESTEasy Api Service</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<version.jetty>9.4.7.v20170914</version.jetty>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${version.jetty}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-core</artifactId>
<version>4.2.0.Final</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>3.9.0.Final</version>
</dependency>
</dependencies>
<build>
<finalName>JettyRestEasyApiService</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>com.foster.app.resteasy.rest.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Here's my Main class:
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher;
public class App {
private static final Logger LOGGER = Logger.getLogger(App.class.getName());
private static final String APPLICATION_PATH = "/jetty/resteasy";
private static final String CONTEXT_ROOT = "/";
private static final Server JETTY_SERVER = new Server(8080);
public App() {}
public static void main(String[] args) throws Exception {
try {
new App().run();
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Application Exception", ex);
throw ex;
} finally {
if (JETTY_SERVER != null) {
JETTY_SERVER.destroy();
}
}
}
public void run() throws Exception {
final ServletContextHandler context = new ServletContextHandler(JETTY_SERVER, CONTEXT_ROOT);
final ServletHolder restEasyServlet = new ServletHolder(new HttpServletDispatcher());
restEasyServlet.setInitParameter("resteasy.servlet.mapping.prefix", APPLICATION_PATH);
restEasyServlet.setInitParameter("javax.ws.rs.Application", JaxRsActivator.class.getCanonicalName());
context.addServlet(restEasyServlet, APPLICATION_PATH + "/*");
final ServletHolder defaultServlet = new ServletHolder(new DefaultServlet());
context.addServlet(defaultServlet, CONTEXT_ROOT);
JETTY_SERVER.start();
LOGGER.log(Level.INFO, "Started Server");
JETTY_SERVER.join();
LOGGER.log(Level.INFO, "Joined Server");
}
}
Here's my Application class:
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
public class JaxRsActivator extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.add(MessageResource.class);
resources.add(TestResource.class);
return resources;
}
}
Here are my POJO classes:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import java.io.Serializable;
import java.util.Date;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "$type")
@JsonSubTypes({
@JsonSubTypes.Type(
value = PojoDog.class,
name = "PojoDog"),
@JsonSubTypes.Type(
value = PojoCat.class,
name = "PojoCat")
})
public abstract class Pojo implements Serializable {
public String property1;
public String property2;
public int property3;
public Date property4;
}
public class PojoCat extends Pojo {
public long sebastian;
}
public class PojoDog extends Pojo {
public String clifford;
}
Here's my endpoint:
@Path("/test")
public class TestResource {
@GET
@Path("pojo")
@Produces(MediaType.APPLICATION_JSON)
public Response getPojo() {
Pojo pojo = new PojoDog();
pojo.property1 = "Hello";
pojo.property2 = "World";
pojo.property3 = 5;
pojo.property4 = new Date();
((PojoDog)pojo).clifford = "I am a dog.";
return Response.ok(pojo).build();
}
}
The class org.jboss.resteasy.util.FindAnnotation
is part of the resteasy-jaxrs
artifact.
See search:
https://search.maven.org/search?q=fc:org.jboss.resteasy.util.FindAnnotation
That pointed me to see that you have mixed resteasy versions.
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-core</artifactId>
<version>4.2.0.Final</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>3.9.0.Final</version>
</dependency>
That's 100% not valid, you need to use the same resteasy version everywhere.
So from that, I took your code and made a few simple changes, and it worked.
I upgraded to Jetty 9.4.20.v20190813.
Upgraded to org.jboss.resteasy/resteasy-jackson2-provider/4.2.0.Final
Removed the two Jackson dependencies from the pom entirely, relied on them coming from transitive dependency on resteasy-jackson2-provider
<!-- REMOVED : no point having them here
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.9</version>
</dependency>
-->
ServletContextHandler
slightly differently (don't pass server into constructor, as that's the job of the handler tree).final ServletContextHandler context = new ServletContextHandler();
final ServletHolder restEasyServlet = new ServletHolder(new HttpServletDispatcher());
context.setContextPath(CONTEXT_ROOT);
restEasyServlet.setInitParameter("resteasy.servlet.mapping.prefix", APPLICATION_PATH);
restEasyServlet.setInitParameter("javax.ws.rs.Application", JaxRsActivator.class.getCanonicalName());
context.addServlet(restEasyServlet, APPLICATION_PATH + "/*");
final ServletHolder defaultServlet = new ServletHolder("default", DefaultServlet.class); // the name "default" here is important
context.addServlet(defaultServlet, "/"); // this is not context-root, it's the default url-pattern
HandlerList handlers = new HandlerList();
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler()); // to report errors that don't match the root context better
JETTY_SERVER.setHandler(handlers);
JETTY_SERVER.start();