The Coder
The Coder

Reputation: 2632

Null Pointer Exception in RestEasy Asynchronous Request

I was working in Resteasy where I've to make a asynchronous request to server. The real purpose is, I'll be submitting a form which will be converted into a .xlsx file which will take atleast 10 seconds to complete. So Asynchronous request is the best way here. I followed the procedures from the following link.

https://docs.jboss.org/resteasy/docs/3.0.9.Final/userguide/html_single/#Asynchronous_HTTP_Request_Processing

I'm making the ajax request like this.

$.ajax({
    url : 'rest/parentPath/childPath',
    type : 'GET',
    success : function(data, status, xhr) {
        console.log(xhr.getResponseHeader('Location'));
    },
    failure : function(data) {
        console.log(data);
    },
    error : function(error,status) {

    }
});

ParentClass.java

import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;

@Component
@Path("/parentPath")
public class ParentClass {
    @GET
    @Path("childPath")
    @Produces("text/plain")
    public void asyncFunction(@Suspended final AsyncResponse response){
        Thread t = new Thread() {
            @Override
            public void run()
            {
                try
                {
                    Response jaxrs = Response.ok("basic").type(MediaType.TEXT_PLAIN).build();         
                    System.out.println("entered======================= =================================================");
                    response.resume(jaxrs);
                }
                catch (Exception e){
                    e.printStackTrace();
                }
            }
        };
        t.start();
    }
}

If I simply make a ajax request, it gives me 503 Service unavailable error but I do get my Asynchronous task executed, I can confirm by seeing my sysout present in wildfly log. But this is not a way how a asynchronous should be done. I've to be able to see the response of my asynchronous task in the second request. I followed the procedures in this link.

https://docs.jboss.org/resteasy/docs/3.0.9.Final/userguide/html_single/#async_job

If I put ?asynch=true in the request url, immediately i get a response of 202 Accepted with a location of asynchronous job in response. But it didn't even entered into the try statement. An error is thrown in the wildfly terminal like this.

19:11:41,733 WARN  [org.jboss.resteasy.core.ExceptionHandler] (pool-4-thread-1) Failed executing GET /parentPath/childPath: org.jboss.resteasy.spi.BadRequestExcept
ion: Failed processing arguments of org.jboss.resteasy.spi.metadata.ResourceMethod@44d4407c
        at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:104) [resteasy-jaxrs-3.0.10.Final.jar:]
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:112) [resteasy-jaxrs-3.0.10.Final.jar:]
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:296) [resteasy-jaxrs-3.0.10.Final.jar:]
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:250) [resteasy-jaxrs-3.0.10.Final.jar:]
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:237) [resteasy-jaxrs-3.0.10.Final.jar:]
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:356) [resteasy-jaxrs-3.0.10.Final.jar:]
        at org.jboss.resteasy.core.AsynchronousDispatcher.invokeSuper(AsynchronousDispatcher.java:237) [resteasy-jaxrs-3.0.10.Final.jar:]
        at org.jboss.resteasy.core.AsynchronousDispatcher$1.call(AsynchronousDispatcher.java:278) [resteasy-jaxrs-3.0.10.Final.jar:]
        at org.jboss.resteasy.core.AsynchronousDispatcher$1.call(AsynchronousDispatcher.java:269) [resteasy-jaxrs-3.0.10.Final.jar:]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) [rt.jar:1.8.0_25]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [rt.jar:1.8.0_25]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [rt.jar:1.8.0_25]
        at java.lang.Thread.run(Thread.java:745) [rt.jar:1.8.0_25]
Caused by: java.lang.NullPointerException
        at org.jboss.resteasy.core.ResourceMethodInvoker.initializeAsync(ResourceMethodInvoker.java:374) [resteasy-jaxrs-3.0.10.Final.jar:]
        at org.jboss.resteasy.core.AsynchronousResponseInjector.inject(AsynchronousResponseInjector.java:43) [resteasy-jaxrs-3.0.10.Final.jar:]
        at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:89) [resteasy-jaxrs-3.0.10.Final.jar:]
        ... 12 more

If I made the same request again with asynch=true, it shows the same error but with (pool-4-thread-2) instead of (pool-4-thread-1)

This means exception is not occured at the server side but at the runtime layer. Coz any exception occured inside my code will be present in log file but not in wildfly terminal. I'll post the web.xml, WebConfig.java, build.gradle files. I'm just replicating the same thing which is done in jboss docs, but I cant figure out why is this exception occuring at the wildfly layer.

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_3_0.xsd" version="3.0">

    <display-name>Web Application</display-name>

    <distributable />

    <listener>
        <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
    </listener>

    <listener>
        <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
    </listener>

    <!-- Context Configuration locations for Spring XML files -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:/applicationContext-resources.xml
            classpath:/applicationContext-dao.xml
            classpath:/applicationContext-service.xml
            classpath*:/applicationContext.xml
            /WEB-INF/applicationContext*.xml
        </param-value>
    </context-param>

    <context-param>
        <param-name>resteasy.servlet.mapping.prefix</param-name>
        <param-value>/rest</param-value>
    </context-param>

    <context-param>
        <param-name>resteasy.async.job.service.enabled</param-name>
        <param-value>true</param-value>
    </context-param>

    <context-param>
        <param-name>resteasy.async.job.service.max.job.results</param-name>
        <param-value>100</param-value>
    </context-param>

    <!-- Maximum wait time on a job when a client is querying for it -->
    <context-param>
        <param-name>resteasy.async.job.service.max.wait</param-name>
        <param-value>300000</param-value>
    </context-param>

    <!-- Thread pool size of background threads that run the job -->
    <context-param>
        <param-name>resteasy.async.job.service.thread.pool.size</param-name>
        <param-value>100</param-value>
    </context-param>

    <!-- Set the base path for the Job uris -->
    <context-param>
        <param-name>resteasy.async.job.service.base.path</param-name>
        <param-value>/asynch/jobs</param-value>
    </context-param>

    <servlet>
        <servlet-name>resteasy-servlet</servlet-name>
        <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.mypackage.service.WebConfig</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>resteasy-servlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>login.html</welcome-file>
    </welcome-file-list>

</web-app>

WebConfig.java

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;

public class WebConfig extends Application {

    private Set<Object> singletons = new HashSet<Object>();
    private Set<Class<?>> empty = new HashSet<Class<?>>();

    public WebConfig() {
        // ADD YOUR RESTFUL RESOURCES HERE
        this.singletons.add(new SignupService());
        this.singletons.add(new UserService());
        this.singletons.add(new ParentClass());
    }

    public Set<Class<?>> getClasses()
    {
        return this.empty;
    }

    public Set<Object> getSingletons()
    {
        return this.singletons;
    }

}

Build.gradle

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'eclipse-wtp'
apply plugin: 'eclipse'

// Uses JDK 8
sourceCompatibility = 1.8
targetCompatibility = 1.8

// 1. Get dependencies from Maven local repository
// 2. Get dependencies from Maven central repository
repositories {
    mavenLocal()
    mavenCentral()
    maven  {
        url "http://repo1.maven.org/maven2"
    }
}

configurations {
    provided
}
  sourceSets {
    main { compileClasspath += configurations.provided }
}

//Project dependencies
dependencies {

    //Spring framework core
    compile 'org.springframework:spring-web:4.1.4.RELEASE'
    compile 'org.springframework:spring-core:4.1.4.RELEASE'
    compile 'org.springframework:spring-context:4.1.4.RELEASE'
    compile 'org.springframework:spring-context-support:4.1.4.RELEASE'
    compile 'org.springframework:spring-orm:4.1.4.RELEASE'

    compile 'org.springframework.security:spring-security-core:4.0.0.RELEASE'

    //MySQL database driver
    //compile 'mysql:mysql-connector-java:5.1.34'
    compile 'com.oracle:ojdbc6:11.2.0.1.0'


    //Hibernate framework 
    compile 'org.hibernate:hibernate-core:4.3.8.Final'
    compile 'commons-dbcp:commons-dbcp:1.2.2'

    //Servlet API
    compile 'javax.servlet:servlet-api:2.5'

    //Base-64 Apache commons
    compile 'commons-codec:commons-codec:1.10'

    //log4j
    compile 'log4j:log4j:1.2.17'
    compile 'org.slf4j:slf4j-simple:1.7.10'

    //XmlBeans Equity Valuation
    compile 'org.apache.xmlbeans:xmlbeans:2.6.0'

    //Poi Equity Valuation
    compile 'org.apache.poi:poi:3.10.1'

    //Poi ooxml Equity Valuation
    compile 'org.apache.poi:poi-ooxml:3.10.1'

    //Poi ooxml Schemas Equity Valuation
    compile 'org.apache.poi:poi-ooxml-schemas:3.10.1'

    //Jacob Equity Valuation
    compile 'jacob:jacob:1.18-M2'

    //Google gson
    compile 'com.google.code.gson:gson:2.3.1'

    provided 'org.jboss.resteasy:resteasy-jaxrs:3.0.11.Final'

    provided 'org.jboss.resteasy:resteasy-spring:3.0.11.Final'

}

Upvotes: 3

Views: 3845

Answers (1)

user1746460
user1746460

Reputation:

Tried to add in the comment but word limit.
I am able to do the asynchronous job now. I found that using the AsynchResponse and the and the Suspended annotations are causing this Exception. this is not required for asynchronous job processing.
The reason for your null pointer is that there is a mix up for the two.

There are two asynchronous things and the documentation really mixes them up
1. Asynchronous response : Using Suspended and AsynchResponse will free the server thread that received the request and the work will be done by the new thread created. But the client will wait for the response. Until your new thread completes and sends it back. For this no changes are required to the web.xml.


2. Asynchronous Job processing : Here the you set the resteasy.async.job.service.enabled to true in the web.xml and any other optional parameters (if required). No other changes are required to the APi. My method was
@POST @Path("/helloworld") public Response getHelloWorld() { log.info("API invoked"); longLiftingJob(); log.info("API invoke done"); return Response.status(200).entity("Hello World").build(); }

With the true parameter in the web.xml the framework will invoke you call in a new thread (simply put) and return with the job id for the same in the 202 response. as Location header as
Location → http://127.0.0.1:8080/myrest/asynch/jobs/1432015827488-1

The client does a GET / POST on the above url and will get the response as returned by the API once the API finishes. But the actual client never waits so asynchronous job.

Upvotes: 1

Related Questions