spring MVC Callable execution continues even after request timeout? - spring

I have an Asynchronous handlermethod like this
#RequestMapping("/custom-timeout-handling")
public #ResponseBody WebAsyncTask<String> callableWithCustomTimeoutHandling() {
Callable<String> callable = new Callable<String>() {
public String call() throws Exception {
while(i==0){
System.out.println("inside while loop->");
}
return "Callable result";
}
};
return new WebAsyncTask<String>(10000, callable);
}
which will execute the while loop until the specified timeout(10sec).
When the request is timeout,it executes the handleTimeout method from TimeoutCallableProcessingInterceptor
public class TimeoutCallableProcessingInterceptor extends CallableProcessingInterceptorAdapter {
#Override
public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
throw new IllegalStateException("[" + task.getClass().getName() + "] timed out");
}
}
Source:i have replaced
Thread.sleep(2000)
with
while(i==0){
System.out.println("inside while loop->");
}
My problem is even after timeout(finished executing handle timeout method)response is send from handletimeout method
the while loop is still processing until the value of i is changed to some other value other than zero.
Is the request is still held by the server?then what is the use of request timeout?
Thanks in advance...

When a servlet container thread detects that a async callable has timed-out, it invokes handleTimeout() (in its own context). Thats the reason you see the handleTimeout() getting executed. It is executed by a servlet container thread and not by the thread that runs the Callable.
If you want custom timeout handling, you need to do two things:
Override onTimeout() in your WebAsyncTask. Whatever callable you provide as the callback to onTimeout() will be invoked within a servlet container thread when it detects your callable has timed-out.
Check for timeouts/interruptions in the Callable you have created inside the controller.
If your Callable does not expect and respect interruption ("If the target thread does not poll the interrupted status the interrupt is effectively ignored"), there is no way to interrupt it! Pls refer this answer to know how to expect and respect interruption.

Related

Run task in background using deferredResult in Spring without frozen browser as client

I have implemented a simple Rest service by which I'd like to test deferredResult from Spring. While am I getting texts in that order:
TEST
TEST 1
TEST AFTER DEFERRED RESULT
I am very interested why in a browser (client) I need to wait that 8 seconds. Isn't that deferedResult shouldn't be non-blocking and run a task in the background? If no, how to create a rest service which will be non-blocking and run tasks in the background without using Java 9 and reactive streams?
#RestController("/")
public class Controller {
#GetMapping
public DeferredResult<Person> test() {
System.out.println("TEST");
DeferredResult<Person> result = new DeferredResult<>();
CompletableFuture.supplyAsync(this::test1)
.whenCompleteAsync((res, throwable) -> {
System.out.println("TEST AFTER DEFERRED RESULT");
result.setResult(res);
});
System.out.println("TEST 1");
return result;
}
private Person test1() {
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Person("michal", 20);
}
}
class Person implements Serializable {
private String name;
private int age;
}
DeferredResult is a holder for a WebRequest to allow the serving thread to release and serve another incoming HTTP request instead of waiting for the current one's result. After setResult or setError methods will be invoked - Spring will release that stored WebRequest and your client will receive the response.
DeferredResult holder is a Spring Framework abstraction for Non-blocking IO threading.
Deferred result abstraction has nothing with background tasks. Calling it without threading abstractions will cause the expected same thread execution. Your test1 method is running in the background because of CompletableFuture.supplyAsync method invocation that gives the execution to common pool.
The result is returned in 8 seconds because the whenCompleteAsync passed callback will be called only after test1 method will return.
You cannot receive the result immediately when your "service call logic" takes 8 seconds despite you are performing it in the background. If you want to release the HTTP request - just return an available proper object (it could contain a UUID, for example, to fetch the created person later) or nothing from the controller method. You can try to GET your created user after N seconds. There are specific HTTP response codes (202 ACCEPTED), that means the serverside is processing the request. Finally just GET your created object.
The second approach (if you should notify your clientside - but I will not recommend you to do it if this is the only reason) - you can use WebSockets to notify the clientside and message with it.

run PublishSubject on different thread rxJava

I am running RxJava and creating a subject to use onNext() method to produce data. I am using Spring.
This is my setup:
#Component
public class SubjectObserver {
private SerializedSubject<SomeObj, SomeObj> safeSource;
public SubjectObserver() {
safeSource = PublishSubject.<SomeObj>create().toSerialized();
**safeSource.subscribeOn(<my taskthreadExecutor>);**
**safeSource.observeOn(<my taskthreadExecutor>);**
safeSource.subscribe(new Subscriber<AsyncRemoteRequest>() {
#Override
public void onNext(AsyncRemoteRequest asyncRemoteRequest) {
LOGGER.debug("{} invoked.", Thread.currentThread().getName());
doSomething();
}
}
}
public void publish(SomeObj myObj) {
safeSource.onNext(myObj);
}
}
The way new data is generated on the RxJava stream is by #Autowire private SubjectObserver subjectObserver
and then calling subjectObserver.publish(newDataObjGenerated)
No matter what I specify for subscribeOn() & observeOn():
Schedulers.io()
Schedulers.computation()
my threads
Schedulers.newThread
The onNext() and the actual work inside it is done on the same thread that actually calls the onNext() on the subject to generate/produce data.
Is this correct? If so, what am I missing? I was expecting the doSomething() to be done on a different thread.
Update
In my calling class, if I change the way I am invoking the publish method, then of course a new thread is allocated for the subscriber to run on.
taskExecutor.execute(() -> subjectObserver.publish(newlyGeneratedObj));
Thanks,
Each operator on Observable/Subject return a new instance with the extra behavior, however, your code just applies the subscribeOn and observeOn then throws away whatever they produced and subscribes to the raw Subject. You should chain the method calls and then subscribe:
safeSource = PublishSubject.<AsyncRemoteRequest>create().toSerialized();
safeSource
.subscribeOn(<my taskthreadExecutor>)
.observeOn(<my taskthreadExecutor>)
.subscribe(new Subscriber<AsyncRemoteRequest>() {
#Override
public void onNext(AsyncRemoteRequest asyncRemoteRequest) {
LOGGER.debug("{} invoked.", Thread.currentThread().getName());
doSomething();
}
});
Note that subscribeOn has no practical effect on a PublishSubject because there is no subscription side-effect happening in its subscribe() method.

Get failure exception in #HystrixCommand fallback method

Is there a way to get the reason a HystrixCommand failed when using the #HystrixCommand annotation within a Spring Boot application? It looks like if you implement your own HystrixCommand, you have access to the getFailedExecutionException but how can you get access to this when using the annotation? I would like to be able to do different things in the fallback method based on the type of exception that occurred. Is this possible?
I saw a note about HystrixRequestContext.initializeContext() but the HystrixRequestContext doesn't give you access to anything, is there a different way to use that context to get access to the exceptions?
Simply add a Throwable parameter to the fallback method and it will receive the exception which the original command produced.
From https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica
#HystrixCommand(fallbackMethod = "fallback1")
User getUserById(String id) {
throw new RuntimeException("getUserById command failed");
}
#HystrixCommand(fallbackMethod = "fallback2")
User fallback1(String id, Throwable e) {
assert "getUserById command failed".equals(e.getMessage());
throw new RuntimeException("fallback1 failed");
}
I haven't found a way to get the exception with Annotations either, but creating my own Command worked for me like so:
public static class DemoCommand extends HystrixCommand<String> {
protected DemoCommand() {
super(HystrixCommandGroupKey.Factory.asKey("Demo"));
}
#Override
protected String run() throws Exception {
throw new RuntimeException("failed!");
}
#Override
protected String getFallback() {
System.out.println("Events (so far) in Fallback: " + getExecutionEvents());
return getFailedExecutionException().getMessage();
}
}
Hopefully this helps someone else as well.
As said in the documentation Hystrix-documentation getFallback() method will be thrown when:
Whenever a command execution fails: when an exception is thrown by construct() or run()
When the command is short-circuited because the circuit is open
When the command’s thread pool and queue or semaphore are at capacity
When the command has exceeded its timeout length.
So you can easily get what raised your fallback method called by assigning the the execution exception to a Throwable object.
Assuming your HystrixCommand returns a String
public class ExampleTask extends HystrixCommand<String> {
//Your class body
}
do as follows:
#Override
protected ErrorCodes getFallback() {
Throwable t = getExecutionException();
if (circuitBreaker.isOpen()) {
// Log or something
} else if (t instanceof RejectedExecutionException) {
// Log and get the threadpool name, could be useful
} else {
// Maybe something else happened
}
return "A default String"; // Avoid using any HTTP request or ypu will need to wrap it also in HystrixCommand
}
More info here
I couldn't find a way to obtain the exception with the annotations, but i found HystrixPlugins , with that you can register a HystrixCommandExecutionHook and you can get the exact exception in that like this :
HystrixPlugins.getInstance().registerCommandExecutionHook(new HystrixCommandExecutionHook() {
#Override
public <T> void onFallbackStart(final HystrixInvokable<T> commandInstance) {
}
});
The command instance is a GenericCommand.
Most of the time just using getFailedExecutionException().getMessage() gave me null values.
Exception errorFromThrowable = getExceptionFromThrowable(getExecutionException());
String errMessage = (errorFromThrowable != null) ? errorFromThrowable.getMessage()
this gives me better results all the time.

Spring AsyncTask: update jsf view component

I have a long run job must run in background and after it finished I want to update jsf view component.
I used SimpleAsyncTaskExecutor to do the work. It works good but when comming to updating ui then I am getting NullPointerException.
Here is my code
SimpleAsyncTaskExecutor tasks = new SimpleAsyncTaskExecutor();
tasks.submitListenable(new Callable<String>() {
#Override
public String call() throws Exception {
//Do long time taking job in approximately 16 seconds
doTheBigJob();
//then update view component by it's id
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add(myComponentId);
return "";
}
});
Not: When the time is short (like 2 seconds), no NullPointerException occurs
Thanks in advence.
FacesContext.getCurrentInstance() returns null because it tries to get the context from thread local variable. But because the executing thread was not initialized by JSF (which is done by javax.faces.webapp.FacesServlet) but created by executor then the thread local variable is null.
I have no idea why NullPointerException does not occur sometimes. By default SimpleAsyncTaskExecutor creates new thread each time unless you specify a thread pool. When I recreated the example it happened every time. Maybe it does but is not logged properly...
To solve your problem you need to resort to polling. For instance you can use property of backing bean to indicate that job was finished.
#Named("someBean")
#SessionScoped
public class SomeBean {
private volatile boolean jobDone = false;
public String execute() {
SimpleAsyncTaskExecutor tasks = new SimpleAsyncTaskExecutor();
tasks.submitListenable(new Callable<String>() {
public String call() throws Exception {
//Do long time taking job in approximately 16 seconds
doTheBigJob();
jobDone = true
return "";
}
});
return null;
}
public boolean isJobDone() {
return jobDone;
}
}
On your page you enter component which is rendered when jobDone==true. For instance:
<h:outputText id="jobDoneText" rendered="#{someBean.jobDone}" value="Job finished"/>
Then using polling and AJAX you update your current page.
In pure JSF the only way to do polling is to use combination of JavaScript and JSF AJAX requests.
Alternatively you can use Primefaces component p:poll to poll for changes.
<p:poll interval="1" update="jobDoneText" />
More about polling in JSF can be found in answers to the following question: JSF, refresh periodically a component with ajax?

Oozie custom asynchronous action

I have a problem implementing a custom asynchronous action in Oozie. My class extends from ActionExecutor, and overwrites the methods initActionType, start, end, check, kill and isCompleted.
In the start method, i want to to start a YARN job, that is implemented through my BiohadoopClient class. To make the call asynchronous, i wrapped the client.run() method in a Callable:
public void start(final Context context, final WorkflowAction action) {
...
Callable<String> biohadoop = new Callable<String>() {
BiohadoopClient client = new BiohadoopClient();
client.run();
}
// submit callable to executor
executor.submit(biohadoop);
// set the start data, according to https://oozie.apache.org/docs/4.0.1/DG_CustomActionExecutor.html
context.setStartData(externalId, callBackUrl, callBackUrl);
...
}
This works fine, and for example when I use my custom action in a fork/join manner, the execution of the actions runs in parallel.
Now, the problem is, that Oozie remains in a RUNNING state for this actions. It seems impossible to change that to a completed state. The check() method is never called by Oozie, the same is true for the end() method. It doesn't help to set the context.setExternalStatus(), context.setExecutionData() and context.setEndData() manually in the Callable (after the client.run() has finished). I tried also to queue manually an ActionEndXCommand, but without luck.
When I wait in the start() method for the Callable to complete, the state gets updated correctly, but the execution in fork/join isn't parallel anymore (which seem logic, as the execution waits for the Callable to complete).
How external clients notify Oozie workflow with HTTP callback didn't help, as using the callback seems to change nothing (well, I can see that it happened in the log files, but beside from that, nothing...). Also, the answer mentioned, that the SSH action runs asynchronously, but I haven't found out how this is done. There is some wrapping inside a Callable, but at the end, the call() method of the Callable is invoked directly (no submission to an Executor).
So far I haven't found any example howto write an asynchronous custom action. Can anybody please help me?
Thanks
Edit
Here are the implementations of initActionType(), start(), check(), end(), the callable implementation can be found inside the start() action.
The callable is submitted to an executor in the start() action, after which its shutdown() method is invoked - so the executor shuts down after the Callable has finished. As next step, context.setStartData(externalId, callBackUrl, callBackUrl) is invoked.
private final AtomicBoolean finished = new AtomicBoolean(false);
public void initActionType() {
super.initActionType();
log.info("initActionType() invoked");
}
public void start(final Context context, final WorkflowAction action)
throws ActionExecutorException {
log.info("start() invoked");
// Get parameters from Node configuration
final String parameter = getParameters(action.getConf());
Callable<String> biohadoop = new Callable<String>() {
#Override
public String call() throws Exception {
log.info("Starting Biohadoop");
// No difference if check() is called manually
// or if the next line is commented out
check(context, action);
BiohadoopClient client = new BiohadoopClient();
client.run(parameter);
log.info("Biohadoop finished");
finished.set(true);
// No difference if check() is called manually
// or if the next line is commented out
check(context, action);
return null;
}
};
ExecutorService executor = Executors.newCachedThreadPool();
biohadoopResult = executor.submit(biohadoop);
executor.shutdown();
String externalId = action.getId();
String callBackUrl = context.getCallbackUrl("finished");
context.setStartData(externalId, callBackUrl, callBackUrl);
}
public void check(final Context context, final WorkflowAction action)
throws ActionExecutorException {
// finished is an AtomicBoolean, that is set to true,
// after Biohadoop has finished (see implementation of Callable)
if (finished.get()) {
log.info("check(Context, WorkflowAction) invoked -
Callable has finished");
context.setExternalStatus(Status.OK.toString());
context.setExecutionData(Status.OK.toString(), null);
} else {
log.info("check(Context, WorkflowAction) invoked");
context.setExternalStatus(Status.RUNNING.toString());
}
}
public void end(Context context, WorkflowAction action)
throws ActionExecutorException {
log.info("end(Context, WorkflowAction) invoked");
context.setEndData(Status.OK, Status.OK.toString());
}
One thing - I can see you are shutting down the executor right after you have submitted the job - executor.shutdown();. That might be causing the issue. Could you please try moving this statement to the end() method instead?
In the end I didn't find a "real" solution to the problem. The solution that worked for me was to implement an action, that invokes the Biohadoop instances in parallel using the Java Executor framework. After the invokation, I wait (still inside the action) for the threads to finish

Resources