Reputation: 8042
I have a JAX-RS webservice that makes use of JPA entity classes. I have a resource class like this:
@Path("/entity")
public class MyEntityResource
{
@GET
@Produces(MediaType.APPLICATION_XML)
@Path("/{entity}")
public MyEntity getMyEntity(@PathParam("entity") String entity)
{
log.debug("Entering getMyEntity with param: " + entity);
MyEntity entityObject = genericService.find(MyEntity.class, entity);
if (entityObject == null)
{
log.debug("Entity not found.");
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
log.debug("Exiting getMyEntity");
return entityObject;
}
}
When I run the service and do a get call on the entity, I get this error:
SEVERE: The response of the WebApplicationException cannot be utilized as the response is already committed. Re-throwing to the HTTP container
javax.ws.rs.WebApplicationException: javax.xml.bind.MarshalException
- with linked exception:
[Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Exception: java.lang.NullPointerException]
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:151)
...
<snip>
...
Caused by: javax.xml.bind.MarshalException
- with linked exception:
[Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Exception: java.lang.NullPointerException]
at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:271)
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:171)
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:149)
... 32 more
Caused by: Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Exception: java.lang.NullPointerException
at org.eclipse.persistence.exceptions.XMLMarshalException.marshalException(XMLMarshalException.java:76)
at org.eclipse.persistence.oxm.XMLMarshaller.marshal(XMLMarshaller.java:502)
at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:269)
... 34 more
Caused by: java.lang.NullPointerException
at org.eclipse.persistence.exceptions.ConversionException.couldNotBeConverted(ConversionException.java:69)
...
<snip>
...
at org.eclipse.persistence.oxm.XMLMarshaller.marshal(XMLMarshaller.java:916)
at org.eclipse.persistence.oxm.XMLMarshaller.marshal(XMLMarshaller.java:468)
... 35 more
Nowhere in any of the stack traces are any of my classes referenced. Also, the "Exiting getMyEntity"
statement is logged before the exception.
I have no idea what is throwing the NPE or how to debug this.
Before this error, I was getting a [com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML:
from my JPA (EclipseLink) entity classes and I added the annotation MOXy @XmlInverseReference to my child class on the parent getter method.
Any thoughts on what could be throwing this exception?
Upvotes: 5
Views: 3969
Reputation: 148977
We have had some offline discussion about this issue, and for the benefit of others that find this post below is the correct setup of using @XmlInverseReference at multiple levels:
Entity A
import java.io.Serializable;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.Set;
@Entity
@XmlRootElement
public class EntityA implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Set<EntityB> entityBs;
@Id
@XmlAttribute
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@OneToMany(mappedBy = "entityABean")
@XmlElement
public Set<EntityB> getEntityBs() {
return this.entityBs;
}
public void setEntityBs(Set<EntityB> entityBs) {
this.entityBs = entityBs;
}
}
Entity B
import java.io.Serializable;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
import java.util.Set;
@Entity
@XmlRootElement
public class EntityB implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Set<EntityC> entityCs;
private EntityA entityABean;
@Id
@XmlAttribute
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne
@JoinColumn(name = "EntityA")
@XmlInverseReference(mappedBy = "entityBs")
public EntityA getEntityABean() {
return this.entityABean;
}
public void setEntityABean(EntityA entityABean) {
this.entityABean = entityABean;
}
@OneToMany(mappedBy = "entityBBean")
@XmlElement
public Set<EntityC> getEntityCs() {
return this.entityCs;
}
public void setEntityCs(Set<EntityC> entityCs) {
this.entityCs = entityCs;
}
}
Entity C
import java.io.Serializable;
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
@Entity
public class EntityC implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private EntityB entityBBean;
@Id
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne
@JoinColumn(name = "EntityB")
@XmlInverseReference(mappedBy = "entityCs")
public EntityB getEntityBBean() {
return this.entityBBean;
}
public void setEntityBBean(EntityB entityBBean) {
this.entityBBean = entityBBean;
}
}
Demo
import java.io.FileInputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(EntityA.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
FileInputStream xml = new FileInputStream("src/test/jaxb/input.xml");
EntityA a = (EntityA) unmarshaller.unmarshal(xml);
for(EntityB b : a.getEntityBs()) {
System.out.println(b.getEntityABean());
for(EntityC c : b.getEntityCs()) {
System.out.println(c.getEntityBBean());
}
}
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(a, System.out);
}
}
Demo - Output
test.jaxb.EntityA@1292d26
test.jaxb.EntityB@196c1b0
test.jaxb.EntityB@196c1b0
test.jaxb.EntityA@1292d26
test.jaxb.EntityB@1e13d52
test.jaxb.EntityB@1e13d52
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<entityA>
<entityBs>
<entityCs/>
<entityCs/>
</entityBs>
<entityBs>
<entityCs/>
<entityCs/>
</entityBs>
</entityA>
This issue is also being handled on the EclipseLink forums, for more information see:
Below is another example of using @XmlInverseReference with a JPA model
Upvotes: 4
Reputation: 4486
Did you include a jaxb.properties file in with your entity classes?
Check out Blaise's answer to this question: JAXB Mapping cyclic references to XML
Hope this helps. RG
Upvotes: 1