Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullPointerException when using CompletionStage fetched by generated proxy #39

Open
coldraydk opened this issue Sep 19, 2017 · 12 comments

Comments

@coldraydk
Copy link

coldraydk commented Sep 19, 2017

New Play application created through sbt running with the following versions:

  • play-soap v. 1.1.3
  • Play v. 2.6.5
  • Scala v. 2.11.11

My Java-classes are generated from the following internal service:
WsdlKeys.wsdlUrls in Compile += url("http://172.x.x.x/SOAP/PortalService.svc?wsdl")

Below is a simple index method in my controller. I've deliberately avoided the return type CompletionStage<Result> on the method for simplicity's sake:

     public Result index() {
        ObjectFactory factory = new ObjectFactory();

        PortalUserDTO portalUserDTO = new PortalUserDTO();
	portalUserDTO.setUsername(factory.createPortalUserDTOUsername("test"));
        portalUserDTO.setFullName(factory.createPortalUserDTOFullName("Test User"));

        ICitizenEndpoint client = portalService.getPortalServiceCitizenEndpoint();
        
        CompletionStage<PortalCitizenDTO> citizen = client.fetchCitizen(10, portalUserDTO);

        // Thread.sleep(500);
        
        citizen.thenApply(x -> x);

        return ok("Everything is fine.");
    }

The NullPointerException appears to be sporadic (roughly 20% of the time), and can be avoided by sleeping the running thread. Below is a stack trace of when the exception is thrown:

Exception in thread "default-workqueue-1" java.lang.NullPointerException
	at org.apache.cxf.endpoint.ClientImpl$1.onMessage(ClientImpl.java:505)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream$1.run(HTTPConduit.java:1185)
	at org.apache.cxf.workqueue.AutomaticWorkQueueImpl$3.run(AutomaticWorkQueueImpl.java:428)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.cxf.workqueue.AutomaticWorkQueueImpl$AWQThreadFactory$1.run(AutomaticWorkQueueImpl.java:353)
	at java.lang.Thread.run(Thread.java:748)

Wireshark shows that the endpoint does provide it with data.

I'm unsure if this behaviour was introduced when you moved from promises to CompletionStages, but I imagine that it is likely. This does prevent me from using the plugin.

@marcospereira
Copy link
Member

Hi @coldraydk, could you provide a small project that reproduces the problem?

Thanks!

@gmethvin
Copy link
Member

You're letting client go out of scope at the end of the method while your CompletionStage is still running, so I suspect the client is getting garbage collected.

I would return a CompletionStage<Result> from the method and write the last part as:

citizen.thenApply(x -> ok("Everything is fine"))

@marcospereira
Copy link
Member

@coldraydk can you confirm that following @gmethvin suggestion fixed the problem?

@coldraydk
Copy link
Author

Hi guys,

Thanks for the feedback. I'm on a tight schedule until Thursday, but I will come back to you at the end of next week.

@ihostage
Copy link
Member

@coldraydk Do you have new information?
@marcospereira Maybe close this issue as outdated?

@lapidus79
Copy link

lapidus79 commented Apr 14, 2020

I encounter this in my Play tests with plays mock server.
Randomly fails, I would say around 20% of the time as well with same Exception in thread "default-workqueue-1" java.lang.NullPointerException

    @Test
    public void dummySoapCall() throws Exception {

        this.server = Server.forRouter(MOCK_PORT, (components) -> RoutingDsl
                .fromComponents(components)
                .POST("/")
                .routingTo((request) -> {

                    Document input = request.body().asXml();
                    assertThat(getElemVal(input, "applicationId")).isEqualTo("abc");

                    return ok().sendResource("dummySoapResponse.xml");
                })
                .build());
        
        dummyService.call().toCompletableFuture().get(5, TimeUnit.SECONDS);
    }
Exception in thread "default-workqueue-1" java.lang.NullPointerException
	at org.apache.cxf.endpoint.ClientImpl$2.onMessage(ClientImpl.java:528)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream$1.run(HTTPConduit.java:1203)
	at org.apache.cxf.workqueue.AutomaticWorkQueueImpl$3.run(AutomaticWorkQueueImpl.java:421)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.apache.cxf.workqueue.AutomaticWorkQueueImpl$AWQThreadFactory$1.run(AutomaticWorkQueueImpl.java:346)
	at java.base/java.lang.Thread.run(Thread.java:834)

@lapidus79
Copy link

lapidus79 commented Apr 20, 2020

After some further investigation this seems to be more common if server is located on same host as client and also seems to occur more frequently on slower machines.

It seems to occur when the server has responded with data and now CXF is about to parse/process this response data. Thus a successful call has been made, we've gotten a response and then when the response data is processed, it dies silently.

It happens somewhere inside Apache CXF. And since a PlaySoap waits for a future to complete this NPE results in the future never completing the our PlaySoap call will eventually result in a TimeoutException with no further info.

When narrowing down using breakpoints at HttpConduit.handleResponseOnWorkqueue and ClientImpl.destroy what seems to happen is that inside handleResponseOnWorkqueue we actually get a "Message received on a Client that has been closed or destroyed." exception. And this is becasue ClientImpl.destroy() is called before handleResponseOnWorkqueue and in the destroy method all resources are cleared.

This seems to be related to https://issues.apache.org/jira/browse/CXF-6940 and this commit https://fisheye.apache.org/changelog/cxf?cs=e0ef66391d2e7e62e63e96214eca95e292271892. More specifically the ClientImpl.java lines

	221         bus = null;
 	222	        conduitSelector = null;
 	223	        outFaultObserver = null;
 	224	        outboundChainCache = null;
 	225	        inboundChainCache = null;
 	226	
 	227	        currentRequestContext = null;
 	228	        requestContext.clear();
 	229	        requestContext = null;
 	230	        responseContext.clear();
 	231	        responseContext = null;
 	232	        executor = null;  

Compiling a new version of CXF with the above lines removed will result in this issue being resolved. So this issue might have been going on for a while. Maybe because of newer JDK / different GC it has become more topical since 2017?

Other CXF ticket which seems to be related to this as well http://cxf.547215.n5.nabble.com/jira-Commented-CXF-7664-Upgrading-to-JDK-1-8-seems-to-cause-concurrency-issues-intermittently-Messagd-td5788109.html

@ennru
Copy link
Contributor

ennru commented Oct 22, 2020

This is most likely fixed with the upgrade of CXF to 3.4.0 in #202 which is now released in Play SOAP 1.2.0 https://github.com/playframework/play-soap/releases/tag/1.2.0

@ennru ennru closed this as completed Oct 22, 2020
@ennru
Copy link
Contributor

ennru commented Oct 23, 2020

As it turns out, the underlying problem is not fixed in CXF 3.4.0.
A workaround seems to be to make sure the JVM holds on to the client instance and does not garbage collect it.

@ennru ennru reopened this Oct 23, 2020
@lapidus79
Copy link

@ennru do

As it turns out, the underlying problem is not fixed in CXF 3.4.0.
A workaround seems to be to make sure the JVM holds on to the client instance and does not garbage collect it.

Do you have any pointer or ideas how this could be achieved?

@ennru
Copy link
Contributor

ennru commented Oct 23, 2020

IIUC, the problem occurs when the client gets garbage collected before the citizen completion stage is completed.

Making the client an instance field, or using it after the stage is completed (eg. log.debug(client.toString());) should help.

  private ICitizenEndpoint client = portalService.getPortalServiceCitizenEndpoint();
   
  void call() {      
        CompletionStage<PortalCitizenDTO> citizen = client.fetchCitizen(10, portalUserDTO);
        
        citizen.thenApply(x -> x);
  }

@picoderen
Copy link

picoderen commented Apr 27, 2021

Has there been any progress regarding the solution of this problem ?

Scala-PlayFw-ApacheCxf / Exception in thread “default-workqueue-1” java.lang.NullPointerException
https://stackoverflow.com/questions/67278814/scala-playfw-apachecxf-exception-in-thread-default-workqueue-1-java-lang-nul

I would like to ask for opinions from those who are interested and have an opinion on the subject. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants