Reputation: 2632
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.
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
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