I am unable to catch thrown exceptions from an async method in Spring. I have written an uncaught exception handler to catch but was unsuccessful.
The application will enable to start any number of forever running asynchronous jobs.
I think my async method needs to return Future so that I can store it in hashmap and check its status or stop the job. I also can get all running jobs by storing it.
I think I can't use get method of future because if the input is correct it blocks and my job will be forever running. I need to send status as started if the input is fine. Whenever an exception occurs in the Async method it is thrown but I am unable to catch it. How can I do that?
Here is my complete code.
Application.java
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
AsyncConfig.java
#EnableAsync
#Configuration
public class AsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
AsyncExceptionHandler.java
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
System.out.println("Exception Cause - " + throwable.getMessage());
System.out.println("Method name - " + method.getName());
for (Object param : obj) {
System.out.println("Parameter value - " + param);
}
}
}
createBucket.java
#Service
public class createBucket {
#Async
public Future<String> start(String config){
try {
JSONObject map = new JSONObject(config);
Jedis jedis = new Jedis(map.getString("jedisip"));
jedis.auth(map.getString("password"));
// code to make a kafka consumer subscribe to a topic given in config input
while(true) {
//forever running code which polls using a kafka consumer
}
}
catch(JedisException j) {
throw new JedisException("Some msg");
}
}
}
Endpoint.java
#Controller
public class Endpoint {
#Autowired
private createBucket service;
private Future<String> out;
private HashMap<String, Future<String>> maps = new HashMap<>();
#PostMapping(value = "/start", consumes = "application/json", produces = "application/json")
public ResponseEntity<String> starttask(#RequestBody String conf) {
try {
out = service.start(conf);
maps.put(conf, out);
}
catch (Exception e) {
return new ResponseEntity<>("exception", HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("{\"started\":\"true\"}", HttpStatus.CREATED);
}
}
As stated in official doc, AsyncUncaughtExceptionHandler is used for void return value.
https://docs.spring.io/spring/docs/5.1.10.RELEASE/spring-framework-reference/integration.html#spring-integration
In your scenario, I recommend using CompletableFuture and DeferredResult:
#Async
public CompletableFuture<String> start(String config) {
CompletableFuture completableFuture = new CompletableFuture();
try {
JSONObject map = new JSONObject(config);
Jedis jedis = new Jedis(map.getString("jedisip"));
jedis.auth(map.getString("password"));
completableFuture.complete("started!");
}
catch(JedisException j) {
completableFuture.completeExceptionally(j);
}
return completableFuture;
}
#PostMapping(value = "/start", consumes = "application/json", produces = "application/json")
public DeferredResult<ResponseEntity> starttask(#RequestBody String conf) {
CompletableFuture<String> start = service.start(conf);
DeferredResult<ResponseEntity> deferredResult = new DeferredResult<>();
start.whenComplete((res, ex) -> {
if (ex == null) {
ResponseEntity<String> successEntity = new ResponseEntity<>("{\"started\":\"true\"}", HttpStatus.CREATED);\
deferredResult.setResult(successEntity);
} else {
// handle ex here!
ResponseEntity<String> exEntity = new ResponseEntity<>("exception", HttpStatus.BAD_REQUEST);
deferredResult.setResult(exEntity);
}
});
return deferredResult;
}
There is another serious problem. The following code is not thread safe.
private Future<String> out;
private HashMap<String, Future<String>> maps = new HashMap<>();
Related
I have a Spring Boot application where I created a POST method that sends data in a streaming fashion to the caller. Code below:
#RequestMapping(value = "/mapmatchstreaming", method = RequestMethod.POST)
public ResponseEntity<StreamingResponseBody> handleRequest(#RequestParam(value = "data", required = true) String data, #RequestParam(value = "mnr", required = true) Boolean mnr) {
logger.info("/mapmatchstreaming endpoint");
try {
Semaphore semaphore = new Semaphore(1);
ObjectMapper mapper = new ObjectMapper();
StreamingResponseBody responseBody = new StreamingResponseBody() {
#Override
public void writeTo (OutputStream outputStream) throws IOException {
// For each map
DataReader dataReader = new DataReader(data, "2020.06.011");
for(String mapRoot: dataReader.getMapsFolders()) {
dataReader = new DataReader(data, "2020.06.011");
DistributedMapMatcherStreaming distributedMapMatcher = new DistributedMapMatcherStreaming(dataReader.getTraces(), mapRoot, dataReader.getBoundingBox());
distributedMapMatcher.mapMatchBatch(new DistributedMapMatcherResult() {
#Override
public void onCorrectlyMapMatched(MapMatchedTrajectory mapMatchedTrajectory) {
try {
semaphore.acquire();
outputStream.write(mapper.writeValueAsString(mapMatchedTrajectory).getBytes());
outputStream.flush();
}
catch (Exception e) {
e.printStackTrace();
logger.error(String.format("Writing to output stream error: %s", e.getMessage()));
} finally{
semaphore.release();
}
}
});
}
}
};
return new ResponseEntity<StreamingResponseBody>(responseBody, HttpStatus.OK);
}
catch (Exception e) {
logger.error(String.format("Map-matching result ERROR: %s", ExceptionUtils.getStackTrace(e)));
return new ResponseEntity<StreamingResponseBody>(HttpStatus.BAD_REQUEST);
}
}
It works nicely, but the problem is that if multiple calls arrive to this method, all of them are run in parallel even if I have set server.tomcat.threads.max=1. In the non-streaming version, every next call waits for the current one to complete.
Is it possible to have blocking streaming calls in Spring? Thanks.
EDIT: I temporarily solved by using a global semaphore with only 1 permit, but I don't think this is the ideal solution.
I ping services nodes and check if it is alive or not. My class, which ping all services is below:
public abstract class PingCallable implements Callable<ResponseEntity<String>> {
#Override
public ResponseEntity call() throws Exception {
try {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> entity = new HttpEntity<>("", headers);
return restTemplate.exchange(new URI(getPingUrl()), HttpMethod.GET, entity, String.class);
} catch (Exception ex) {
ex.printStackTrace();
return new ResponseEntity("Exception occured while ping. Exception message = " + ExceptionUtil.getFullCauseMessage(ex), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
protected abstract String getPingUrl();
}
public class ServicePingCallable extends PingCallable {
private Node node;
public ServicePingCallable(Node node) {
this.node = node;
}
#Override
protected String getPingUrl() {
return "//" + node.getIp() + "/api/" + node.getService().getServicePath() + "/pingService";
}
public Node getNode(){
return this.node;
}
}
Method which pings service nodes is here:
#Scheduled(fixedDelayString = "${config.ping.fixedDelay}")
public void pingServices() throws InterruptedException {
List<Node> nodeList = serviceRepository.findAll();
List<ServicePingCallable> callableList = new ArrayList<>(nodeList.size());
ExecutorService executor = Executors.newFixedThreadPool(10);
try {
for (Node node : nodeList) {
ServicePingCallable pingCallable = new ServicePingCallable(node);
callableList.add(pingCallable);
}
List<Future<ResponseEntity<String>>> responses = executor.invokeAll(callableList, 10, TimeUnit.SECONDS);
for(Future<ResponseEntity<String>> response: responses){
// here I should check response HttpStatus and response text and save node ping state.
// But how I can know here for which Node is response?
//It whould be great to have such method:
//ServicePingCallable callable = (ServicePingCallable)response.getCallable();
//Node = callable.getNode();
}
} catch (Exception ex) {
//catch and log here
} finally {
if (!executor.isShutdown()) {
try {
executor.shutdown();
} catch (Exception ex1) {
}
}
}
I can add to PingCallable private filed ResponseEntity and return from call() the PingCallable but this approach is like workaround and not clear.
I think this is common task and should be some pattern how to get the callable from the future result. If you know it please share.
I have 7 threads running in an ExecutorPool that process data and occasionally need data from a listener instance running on another thread. The listener sends a request over a socket to a server and a while later, when the result is returned, the listener will return the data to the worker thread that called it. I want to block the worker thread until the requested data is returned, but I don't want to block the listener from making other requests from the other worker threads. How do I do that?
If one thread hands off work to another thread, and then subsequently simply waits for the result, you don't need another thread to do the work. You may need a class that does the work, but which is called on the same thread. And if the same instance is used by multiple threads some synchronization may be needed. But the bottom line is this :
You don't need the listener thread. Replace it with a component that handles a request, and call it synchronously.
Edit
Given your own answer, your problem is a bit clearer. As #JimN suggests you probably want to hand out a Future to the worker thread, and make it a CompletableFuture the Listener keeps in a Map keyed by request id until the response returns.
Sample code :
public class WorkUnitProcessor implements Runnable {
// ...
#Override
public void run() {
while(true) {
WorkUnit work = master.getNextWorkUnit();
if(work == null) return;
doWork(work);
}
}
public void doWork(WorkUnit work) {
//Do some work...
try {
DataRequest dataRequest = createRequest(work);
Future<Response> future = server.getData(dataRequest);
Response response = future.get(); // this call blocks until the Response is available.
//finish doing work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
// handle e.getCause()
}
}
// ...
}
public class Server implements DataSourceDrivenCallback {
private final DataSource dataSource;
private Map<Integer, CompletableFuture<Response>> openRequests = new ConcurrentHashMap<>();
public Server(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
public void incomingDataCallback(int requestId, ChunkOfData requestedData) {
CompletableFuture<Response> responseHolder = openRequests.remove(requestId); // get the responseHolder
if (responseHolder != null) {
responseHolder.complete(toResponse(requestedData)); // make the response available.
}
}
public Future<Response> getData(DataRequest datarequest) {
int requestId = dataSource.submitRequest(serializeAndTranslateRequest(datarequest));
CompletableFuture<Response> future = new CompletableFuture<>();
openRequests.put(requestId, future);
return future;
}
// ...
}
I think this might work. What I was looking for is described here:
https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
It's the ability to make a thread sleep until it is notified by the thread that it is waiting on. Seems easy to use.
public class DataProcessor {
private List<WorkUnit> work;
private Server server;
public DataProcessor(List<WorkUnit> work, int numprocessors) {
this.work = work;
setupProcessors(numprocessors);
Server server = new Server();
}
private void setupProcessors(int numprocessors) {
for(int i = 0; i < numprocessors; i++) {
WorkUnitProcessor worker = new WorkUnitProcessor(this, server);
worker.start();
}
}
public synchronized WorkUnit getNextWorkUnit() {
if(work.isEmpty()) return null;
return work.remove(0);
}
}
public class WorkUnitProcessor(Server server) {
private DataProcessor master;
private Server server;
public WorkUnitProcessor(DataProcessor master) {
this.master = master;
}
#Override
public void run() {
while(true) {
WorkUnit work = master.getNextWorkUnit();
if(work == null) return;
doWork(work);
}
}
public void doWork(WorkUnit work) {
//Do some work...
server.getData(datarequest, this);
while(!datarequest.filled) {
try {
wait();
} catch (InterruptedException e) {}
}
//finish doing work
}
}
public class Server implements DataSourceDrivenCallback {
private DataSource ds;
private Map<Integer, OpenRequest> openrequests;
public Server() {
//setup socket and establish communication with server through DataSource object
DataSource ds = new DataSource(<ID>, <Socket>);
}
public synchronized void getData(DataRequest datarequest, WorkUnitProcessor workerthread) {
int requestid = ds.submitRequest(serializeAndTranslateRequest(datarequest));
openrequests.add(new OpenRequest(workerthread, datarequest));
}
#Override
public void incomingDataCallback(int requestid, ChunkOfData requesteddata) {
OpenRequest request = openrequests.get(requestid);
request.datarequest.storeData(requesteddata);
request.workerthread.notify();
}
}
public class OpenRequest {
private WorkUnitProcessor workerthread;
private DataRequest datarequest;
//other details about request
}
apologies for the basic question; I'm new to the Java world and the spring framework. I've built a little example application that makes a bunch of async requests to an external service and returns a list of the responses ('metrics'), but I need to make my application wait until all the responses have come back. Right now I have a (don't hate me) Thread.sleep while I let the results come back, but obviously this is very nasty. Can anyone suggest a better way of architecting this?
Calling class:
#Service
public class MetricService {
#Autowired
private MetricProcessor processor;
private LinkedBlockingQueue<Metric> queue;
#Scheduled(fixedDelay = 60000)
public void queryExternalService() {
List<Metrics> metrics = new ArrayList<>();
metrics = processor.getMetrics();
//this is horrible and I'm a horrible human being
try {
Thread.sleep(10000); //wait for the requests to come back
}
catch (Exception e) {
e.printStackTrace();
}
queue.addAll(metrics);
}
}
Class:
#Component
public class MetricProcessor {
#Autowired
private AsyncClient externalClient;
public List<Metrics> getMetrics() {
List<Metrics> returnObj = new Arraylist<>();
for(Blah blah : bleh) {
Request request = new Request("abc");
externalClient.getMetricAsync(request, new AsyncHandler<request, result>() {
#Override
public void onError(Exception e) {
System.out.println("Error");
}
#Override
public void onSuccess(Request request, Result result) {
returnObj.add(new Metric(result.getKey(), result.getValue()));
}
});
}
return returnObj;
}
}
Any help would be greatly appreciated!
Try a Future.
In MetricService:
public void queryExternalService() {
Future<List<Metrics>> metricsFuture = processor.getMetrics();
try {
queue.addAll(metricsFuture.get(60, TimeUnit.SECONDS));
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
}
So notice instead of the desired List, your processor provides a reference to a Future which may fulfil that request later:
public Future<List<Metrics>> getMetrics() {
MetricsFuture metricsFuture = new MetricsFuture();
// Need to ask for the metrics to be built
metricsFuture.buildMetrics();
return metricsFuture;
}
private static class MetricsFuture extends AbstractFuture<List<Metrics>> {
// Assuming the requests are asynchronous, this should be a thread-safe list
List<Metrics> returnObj = new CopyOnWriteArrayList<>();
void buildMetrics() {
for(Blah blah : bleh) {
final Request request = new Request("abc");
externalClient.getMetricAsync(request, new AsyncHandler<request, result>() {
#Override
public void onError(Exception e) {
onError(request, e);
}
#Override
public void onSuccess(Request request, Result result) {
addMetrics(new Metrics(result.getKey(), result.getValue()));
}
});
}
}
void onError(Request request, Exception e) {
// Is any error a total failure? This allows us to terminate waiting
setException(e); // alternative we could remove request or keep a list of errors
System.out.println("Error");
}
void addMetrics(Metrics metric) {
returnObj.add(metric);
// Once we have received the expected number of results we can pass that prepare that
// as a result of this future.
if(returnObj.size() == bleh.size()) {
set(returnObj);
}
}
}
I have a long running task which as a result generates regular files and a main file that lists others.
Scheduler regenerates this files once in a day through cron.
The task flow is implemented using rx-java.
The problem is if one request comes in and starts the task or the task gets ran by a scheduler and then while task is in progress some other request comes and don't wait for task to complete, instead firing another execution.
So the question is how to sync on task execution, so it will be done only once ?
This is sample code:
#Service
public class FileService {
#Autowired FileRepository fileRepository;
#Autowired List<Pipeline> pipelines;
public Observable<File> getMainFile() {
if (fileRepository.isMainFileExists())
return Observable.just(fileRepository.getMainFile());
else
return generate(() -> fileRepository.getMainFile());
}
public Observable<File> getFile(String fileName) {
if (fileRepository.isMainFileExists())
return Observable.just(fileRepository.getFile(fileName));
else
return generate(() -> fileRepository.getFile(fileName));
}
Observable<File> generate(Func0<File> whenGenerated) {
return Observable.from(pipelines)
// other business logic goes here
// after task execution finished just get needed file
.map(isAllPipelinesSuccessful -> {
return whenGenerated.call();
});
}
#Scheduled(cron = "0 0 4 * * ?")
void scheduleGeneration() {
generate(() -> fileRepository.getMainFile()).subscribe();
}
}
And it's called from controller, sample code below:
#RestController
public class FileController {
private static final Long TIMEOUT = 1_000 * 60 * 10L; //ten mins
#Autowired FileService fileService;
#RequestMapping(value = "/mainfile", produces = "application/xml")
public DeferredResult<ResponseEntity<InputStreamResource>> getMainFile() {
DeferredResult<ResponseEntity<InputStreamResource>> deferredResult = new DeferredResult<>(TIMEOUT);
Observable<File> observableMainFile = fileService.getMainFile();
observableMainFile
.map(this::fileToInputStreamResource)
.map(resource -> ResponseEntity.ok().cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic()).body(resource))
.subscribe(deferredResult::setResult, ex -> {
deferredResult.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null));
});
return deferredResult;
}
#RequestMapping(value = "/files/{filename:.+}", produces = "application/xml")
public DeferredResult<ResponseEntity<InputStreamResource>> getFile(#PathVariable("filename") String filename) {
DeferredResult<ResponseEntity<InputStreamResource>> deferredResult = new DeferredResult<>(TIMEOUT);
Observable<File> observableFile = fileService.getFile(filename);
observableFile
.map(this::fileToInputStreamResource)
.map(resource -> ResponseEntity.ok().cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic()).body(resource))
.subscribe(deferredResult::setResult, ex -> {
boolean isFileNotFound = FileNotFoundException.class.isInstance(ex.getCause());
HttpStatus status = isFileNotFound ? HttpStatus.NOT_FOUND : HttpStatus.INTERNAL_SERVER_ERROR;
deferredResult.setErrorResult(ResponseEntity.status(status).body(null));
});
return deferredResult;
}
}
I have something like the following, but I think there are way better solutions to this. I am using RxJava2-RC5.
Answer lacks the checking, that task has been executed.
https://gist.github.com/anonymous/7b4717cea7ddce270a2e39850a3bd2a4
UPDATE::
interface FileRepository {
String getFile();
Boolean isMainFileExists();
}
private static Scheduler executorService = Schedulers.from(Executors.newFixedThreadPool(1));
#org.junit.Test
public void schedulerTest123() throws Exception {
FileRepository fRepo = mock(FileRepository.class);
when(fRepo.getFile()).thenReturn("");
when(fRepo.isMainFileExists()).thenReturn(false);
Thread t1 = new Thread(() -> {
getFile(fRepo, executorService).subscribe();
});
Thread t2 = new Thread(() -> {
getFile(fRepo, executorService).subscribe();
});
t1.start();
t2.start();
Thread.sleep(3_000);
when(fRepo.getFile()).thenReturn("DasFile");
when(fRepo.isMainFileExists()).thenReturn(true);
Thread t3 = new Thread(() -> {
getFile(fRepo, executorService).subscribe();
});
t3.start();
Thread.sleep(5_000);
}
private Observable<String> getFile(FileRepository fileRepo, Scheduler scheduler) {
return Observable.defer(() -> {
try {
if (fileRepo.isMainFileExists()) {
return Observable.fromCallable(fileRepo::getFile)
.subscribeOn(Schedulers.io())
.doOnNext(s -> printCurrentThread("Get File from Repo"));
} else {
return startLongProcess().doOnNext(s -> printCurrentThread("Push long processValue"));
}
} catch (Exception ex) {
return Observable.error(ex);
}
}).subscribeOn(scheduler).doOnSubscribe(disposable -> printCurrentThread("SUB"));
}
private Observable<String> startLongProcess() {
return Observable.fromCallable(() -> {
printCurrentThread("Doing LongProcess");
Thread.sleep(5_000);
return "leFile";
});
}
private void printCurrentThread(String additional) {
System.out.println(additional + "_" + Thread.currentThread());
}