Null pointer exception only on application startup

2019-08-10 06:31发布

问题:

I have a very strange problem. I am creating a JPA (Eclipselink) application, with a Jersey rest service, and an angularJS front end.

When the application starts up, when I go to my angular page, I get a marshalling exception which seems to be caused by a null pointer somewhere. Press refresh in my browser, and the data comes back - no exceptions, and I am good to go from there on in.

I am using @XMLInverseReference on a one to many join, and the way I have set the annotations is the only way I can get parseable JSON with a back reference. It must be all set up correctly, as after the exception is raised the application works across Get, Post and Put so I have a workable CRUD interface.

Exception is:

WARNING: WebApplicationException cause: javax.xml.bind.MarshalException - with linked exception: [Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): 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:457) at org.eclipse.persistence.jaxb.rs.MOXyJsonProvider.writeTo(MOXyJsonProvider.java:840) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:243) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:230) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:149) at org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:103) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:149) at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:88) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:149) at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1139) at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:557) at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:381) at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:371) at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:262) at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271) at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267) at org.glassfish.jersey.internal.Errors.process(Errors.java:315) at org.glassfish.jersey.internal.Errors.process(Errors.java:297) at org.glassfish.jersey.internal.Errors.process(Errors.java:267) at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:318) at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:236) at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:983) at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:361) at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:372) at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:335) at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:218) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:176) at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:145) at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:92) at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:389) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:315) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:744) Caused by: Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): 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:98) at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.java:897) at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.java:862) at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:455) ... 50 more Caused by: java.lang.NullPointerException at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.marshal(XMLCompositeObjectMappingNodeValue.java:145) at org.eclipse.persistence.internal.oxm.NodeValue.marshal(NodeValue.java:102) at org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext.marshal(ObjectMarshalContext.java:59) at org.eclipse.persistence.internal.oxm.XPathNode.marshal(XPathNode.java:401) at org.eclipse.persistence.internal.oxm.XPathObjectBuilder.buildRow(XPathObjectBuilder.java:240) at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:118) at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:1) at org.eclipse.persistence.internal.oxm.XMLCompositeCollectionMappingNodeValue.marshalSingleValue(XMLCompositeCollectionMappingNodeValue.java:328) at org.eclipse.persistence.internal.oxm.XMLCompositeCollectionMappingNodeValue.marshal(XMLCompositeCollectionMappingNodeValue.java:108) at org.eclipse.persistence.internal.oxm.NodeValue.marshal(NodeValue.java:149) at org.eclipse.persistence.internal.oxm.NodeValue.marshal(NodeValue.java:102) at org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext.marshal(ObjectMarshalContext.java:59) at org.eclipse.persistence.internal.oxm.XPathNode.marshal(XPathNode.java:401) at org.eclipse.persistence.internal.oxm.XPathObjectBuilder.buildRow(XPathObjectBuilder.java:240) at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:118) at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:1) at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.marshalSingleValue(XMLCompositeObjectMappingNodeValue.java:249) at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.marshal(XMLCompositeObjectMappingNodeValue.java:150) at org.eclipse.persistence.internal.oxm.NodeValue.marshal(NodeValue.java:102) at org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext.marshal(ObjectMarshalContext.java:59) at org.eclipse.persistence.internal.oxm.XPathNode.marshal(XPathNode.java:401) at org.eclipse.persistence.internal.oxm.XPathObjectBuilder.buildRow(XPathObjectBuilder.java:240) at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:118) at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:1) at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.java:751) at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.java:571) at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshalStreamOrWriter(XMLMarshaller.java:1107) at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.java:1079) at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.java:892) ... 52 more

My "One" side of my JPA entity is:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PersistenceUnit;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import org.eclipse.persistence.oxm.annotations.XmlInverseReference;

@Entity(name = "PR_GPT")
@Table
@XmlRootElement
@XmlType(propOrder={"id", "gptDesc", "therapyArea"})
@PersistenceUnit(unitName = "graps-jpa")
public class PrGPT {

    @ManyToOne
    @JoinColumn(name="THERAPY_AREA")
    @XmlInverseReference(mappedBy="gpts")   
    @XmlElement
    protected PrTherapyArea therapyArea;

    @Id
    @Column(name = "ID")    
    @GeneratedValue(generator = "prGPTSequence", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "prGPTSequence", sequenceName = "PR_GPT_SEQ", allocationSize=50)  
    @XmlElement
    private Long id;

    @Column(name = "GPT_DESC", length = 255)
    @Size(min = 1, max = 255)
    @XmlElement
    private String gptDesc;

    public void setTherapyArea(PrTherapyArea therapyArea) {
        this.therapyArea = therapyArea;     
    }


    public PrTherapyArea getTherapyArea() {
        return this.therapyArea;
    }


    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }


    public String getGptDesc() {
        return gptDesc;
    }

    public void setGptDesc(String gptDesc) {
        this.gptDesc = gptDesc;
    }

    public PrGPT() {
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("GPT [");
        sb.append("GPT ID : ").append(id).append(", GPT Desc : ")
                .append(gptDesc).append("]");
        return sb.toString();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((this.getId() == null) ? 0 : this.getId().hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        PrGPT other = (PrGPT) obj;
        if (this.getId() == null) {
            if (other.getId() != null)
                return false;
        } else if (this.getId().compareTo(other.getId()) != 0)
            return false;
        return true;
    }

}

My many side is as below:

@Entity(name = "PR_THERAPY_AREA")
@Table
@XmlRootElement
@PersistenceUnit(unitName = "graps-jpa")
public class PrTherapyArea {



    @Id
    @Column(name = "ID")
    @GeneratedValue(generator = "prTASequence", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "prTASequence", sequenceName = "PR_TA_SEQ", allocationSize=50)    
    @XmlElement 
    private Long id;

    @Column(name = "THERAPY_AREA", length = 255)
    @Size(min = 1, max = 255)
    @XmlElement 
    private String therapyArea;


    public Long getId() {
        return id;
    }


    @OneToMany(mappedBy="therapyArea", orphanRemoval = true, cascade = { javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.MERGE }, fetch = FetchType.EAGER)
    @XmlElement     
    protected List<PrGPT> gpts;

    public void setId(Long id) {
        this.id = id;
    }


    public List<PrGPT> getGpts() {
        return this.gpts;
    }

    public void setGpts(List<PrGPT> gpts) {
        this.gpts = gpts;
    }


    public String getTherapyArea() {
        return therapyArea;
    }

    public String setTherapyArea() {
        return therapyArea;
    }

    public PrTherapyArea() {
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("THERAPY AREA [");
        sb.append("THERAPY_AREA ID : ").append(id)
                .append(", Therapy Area Desc : ").append(therapyArea)
                .append("]");
        return sb.toString();
    }


    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((this.getId() == null) ? 0 : this.getId().hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        PrTherapyArea other = (PrTherapyArea) obj;
        if (this.getId() == null) {
            if (other.getId() != null)
                return false;
        } else if (this.getId().compareTo(other.getId()) != 0)
            return false;
        return true;
    }


}

I tried without adding @XMLInverseReference on the many side in TherapyArea back to GPT, but the JSON is badly formed (I raised a SO question on this but didn't get a response unfortunately @XmlInverseReference - invalid token in json in a bidirectional JPA relationship and this is the only way to get a good json response).

My json response is fine, with the back reference i need to the TherapyArea through @XMLInverseReference:

GPT Get response:

[{"id":1452,"gptDesc":"testing 1-2-3","therapyArea":{"id":2,"therapyArea":"Infection"}},{"id":1454,"gptDesc":"test 99","therapyArea":{"id":3,"therapyArea":"CVMD"}},{"id":1456,"gptDesc":"testing 1-2-3","therapyArea":{"id":2,"therapyArea":"Infection"}},{"id":1457,"gptDesc":"steves GPTss","therapyArea":{"id":5,"therapyArea":"Immuno-Oncology"}},{"id":1459,"gptDesc":"Debs GPT","therapyArea":{"id":2,"therapyArea":"Infection"}},{"id":1460,"gptDesc":"testing","therapyArea":{"id":2,"therapyArea":"Infection"}},{"id":1465,"gptDesc":"Test Add 3","therapyArea":{"id":3,"therapyArea":"CVMD"}},{"id":1466,"gptDesc":"Test Add 4","therapyArea":{"id":3,"therapyArea":"CVMD"}},{"id":1951,"gptDesc":"Test Add 56666666666666666666","therapyArea":{"id":1,"therapyArea":"Oncology"}},{"id":1952,"gptDesc":"Test Add 6","therapyArea":{"id":4,"therapyArea":"Respiratory and Inflammation"}},{"id":2002,"gptDesc":"Test add 9","therapyArea":{"id":1,"therapyArea":"Oncology"}},{"id":2003,"gptDesc":"test add 11","therapyArea":{"id":1,"therapyArea":"Oncology"}},{"id":2004,"gptDesc":"test add 12","therapyArea":{"id":2,"therapyArea":"Infection"}},{"id":2005,"gptDesc":"test","therapyArea":{"id":1,"therapyArea":"Oncology"}},{"id":2006,"gptDesc":"test","therapyArea":{"id":2,"therapyArea":"Infection"}},{"id":2007,"gptDesc":"test","therapyArea":{"id":2,"therapyArea":"Infection"}},{"id":2008,"gptDesc":"test","therapyArea":{"id":3,"therapyArea":"CVMD"}},{"id":2009,"gptDesc":"Test","therapyArea":{"id":1,"therapyArea":"Oncology"}},{"id":2010,"gptDesc":"test","therapyArea":{"id":3,"therapyArea":"CVMD"}},{"id":2012,"gptDesc":"Test","therapyArea":{"id":3,"therapyArea":"CVMD"}},{"id":1451,"gptDesc":"Testing1234567890909pp","therapyArea":{"id":3,"therapyArea":"CVMD"}},{"id":1453,"gptDesc":"testing 123456","therapyArea":{"id":3,"therapyArea":"CVMD"}},{"id":1455,"gptDesc":"Test 1012","therapyArea":{"id":4,"therapyArea":"Respiratory and Inflammation"}}]

Therapy Areas response:

[{"id":1,"therapyArea":"Oncology","gpts":[{"id":1951,"gptDesc":"Test Add 56666666666666666666"},{"id":2002,"gptDesc":"Test add 9"},{"id":2003,"gptDesc":"test add 11"},{"id":2005,"gptDesc":"test"},{"id":2009,"gptDesc":"Test"}]},{"id":2,"therapyArea":"Infection","gpts":[{"id":1452,"gptDesc":"testing 1-2-3"},{"id":1456,"gptDesc":"testing 1-2-3"},{"id":1459,"gptDesc":"Debs GPT"},{"id":1460,"gptDesc":"testing"},{"id":2004,"gptDesc":"test add 12"},{"id":2006,"gptDesc":"test"},{"id":2007,"gptDesc":"test"}]},{"id":3,"therapyArea":"CVMD","gpts":[{"id":1454,"gptDesc":"test 99"},{"id":1465,"gptDesc":"Test Add 3"},{"id":1466,"gptDesc":"Test Add 4"},{"id":2008,"gptDesc":"test"},{"id":2010,"gptDesc":"test"},{"id":2012,"gptDesc":"Test"},{"id":1451,"gptDesc":"Testing1234567890909pp"},{"id":1453,"gptDesc":"testing 123456"}]},{"id":4,"therapyArea":"Respiratory and Inflammation","gpts":[{"id":1952,"gptDesc":"Test Add 6"},{"id":1455,"gptDesc":"Test 1012"}]},{"id":5,"therapyArea":"Immuno-Oncology","gpts":[{"id":1457,"gptDesc":"steves GPTss"}]}]

I have a jaxb.properties file set up in the same package as my entity models as I know I need to.

Don't really understand what is going on.

I don't think it has anything to do with caching on the client, which was suggested by a colleague. Could be I guess, but I suspect it's more to do with the @XmlInverseReference.

UPDATE:

On application restart, going straight for the rest end point url, I get a response for both entities, without an exception being raised. Then when I go to the angular page, the exception doesnt get raised.

Go to my crud page as the first action after an app restart, I get the marshalling exception, refresh then it is fine.

Almost like I need to "prime" it before it will work?! But going to the restful endpoint has nothing to do with angular, its a pure jersey response....

UPDATE 2:

This seems to resolve the error:

//call the TA service to get the TA list for the drop down lists
            prTAService.getTAs().then(function(tas) {
                $scope.therapyAreas = tas;
                prGPTService.getGPTs().then(function(gpts) {
                    $scope.prGpts = gpts;
                });
            });

i.e. only get one set of resources, after the first set of resources has loaded.....i.e. a then inside a then!? Seems odd.

回答1:

I've seen this before when JAXB is first instantiated concurrently. There are some race conditions where it fails to initialize properly if it's started from multiple threads. I can imagine that happening from an angular page that made simultaneous backend calls.

Not a lot you can do apart from hit the endpoint once from curl (or whatever) before using the browser.