Reputation: 31
I am very new to building rest API application. Here I was trying to make a simple method return a response xml of a model object Message
Here is my web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.rest.messenger.service</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/webapi/*</url-pattern>
</servlet-mapping>
</web-app>
I have a service class that returns a hard-coded list of Message
objects.
package com.rest.messenger.service;
import java.util.ArrayList;
import java.util.List;
import com.rest.messenger.model.Message;
public class MessageService {
public List<Message> getAllMessages(){
Message m1 = new Message(1L, "message1", "mt");
Message m2 = new Message( 2L, "message 2", "pt");
List<Message> messages = new ArrayList<Message>();
messages.add(m1);
messages.add(m2);
return messages;
}
}
The Message
object is defined as follows ( here I used @XmlRootElement
annotation to let jaxb convert this into xml, as I wanted an xml response to be returned by resource):
package com.rest.messenger.model;
import java.util.Date;
import jakarta.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Message {
private long id;
private String message;
private String author;
private Date created;
public Message() {
}
public Message(long id, String message, String author) {
super();
this.id = id;
this.message = message;
this.author = author;
this.created = new Date();
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
}
I mapped the incoming GET
request to a resource as follows:
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/messages")
public class MessageResource {
public MessageService messageService = new MessageService();
@GET
@Produces(MediaType.APPLICATION_XML)
public List<Message> getMessages() {
return messageService.getAllMessages();
}
}
Now when I run this and try to access the resource at URL http://localhost:8080/messenger.service/webapi/messages
I get the following internal server error:
May 06, 2020 1:28:14 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [Jersey Web Application] in context with path [/messenger.service] threw exception [java.lang.NullPointerException] with root cause
java.lang.NullPointerException
at jakarta.xml.bind.ContextFinder.handleClassCastException(ContextFinder.java:114)
at jakarta.xml.bind.ContextFinder.newInstance(ContextFinder.java:254)
at jakarta.xml.bind.ContextFinder.newInstance(ContextFinder.java:240)
at jakarta.xml.bind.ContextFinder.find(ContextFinder.java:375)
at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:691)
at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:632)
at org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getStoredJaxbContext(AbstractJaxbProvider.java:288)
at org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getJAXBContext(AbstractJaxbProvider.java:273)
at org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getMarshaller(AbstractJaxbProvider.java:240)
at org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getMarshaller(AbstractJaxbProvider.java:207)
at org.glassfish.jersey.jaxb.internal.AbstractCollectionJaxbProvider.writeTo(AbstractCollectionJaxbProvider.java:243)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:242)
The tutorial that I followed used javax and I believe it is now migrated to jakarta.
Not sure if it has to do with @XmlRootElement
I also added dependencies in pom.xml
as suggested on:
jersey return 500 when trying return an XML
I used the modifiers for fields of model object to be private, as was suggested.( the default XML accessor type used jaxb is able to work on private fields)
Can anyone please suggest on this?
Upvotes: 1
Views: 2016
Reputation: 31
After numerous attempts on resolving this, I got the solution:
A bit of background scenario of what causes java.lang.NullPointerException
with the stack trace showing handleClassCastException
while using JAXB
can be found at https://github.com/javaee/jaxb-v2/issues/863 with something similar happening.
So basically, there were more than one copies of APIs for JAXB at once. My application had Jakarta API as part of jersey dependency ( Jakarta is now standard API for Java EE) but again till JDK 8, JAXB was also part of Java extended APIs in classpath ( javax support). I was using jakarta api for imports of JAXB annotations and classes, but also was using jdk 8 for compilation, thus causing the issue. Coming to use of jersey, I was implementing the REST API using pom dependency of Jersey 3.0.0 ( which is still "too new" to be considered a stable release)
Resolution could be the use of javax for all the api and imports if you are going to stick with JDK 8 - and downgrading jersey version in pom
(I used 2.3.1) [ or the external JAR version if you are not on maven)
Note that a higher jersey version - 3.0.0 comes bundled with jakarta APIs only and you will have shift your imports from javax to jakarta in such cases.
Second option is if you intend to upgrade the JDK version higher than 1.8, you should get a NoClassDefFoundError
, but that can be resolved if you are still using a JDK version not greater than 10. Jaxb API is now supposed to be an API of Java EE and classpath doesn't contain them by default. There is a way of enabling them however:
How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException in Java 9
In my case, I provided support for JAXB implementation classes by making entry in pom.xml:
To summarize I did following:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
<scope>runtime</scope>
</dependency>
Upvotes: 2