Suppose I have a two classes that work together to execute a callable like this:
public class blah {
#Autowired
private ExecutorServiceUtil executorServiceUtil;
#Autowired
private RestTemplate restClient;
public SomeReturnType getDepositTransactions(HttpHeaders httpHeaders) {
ExecutorService executor = executorServiceUtil.createExecuter();
try {
DepositTransactionsAsyncResponse asyncResponse = getPersonalCollectionAsyncResponse( httpHeaders, executor);
// do some processing
// return appropriate return type
}finally {
executorServiceUtil.shutDownExecutor(executor);
}
}
Future<ResponseEntity<PersonalCollectionResponse>> getPersonalCollectionAsyncResponse( HttpHeaders httpHeaders, ExecutorService executor) {
PersonalCollectionRequest personalCollectionRequest = getpersonalCollectionRequest(); // getPersonalCollectionRequest populates the request appropriately
return executor.submit(() -> restClient.exchange(personalCollectionRequest, httpHeaders, PersonalCollectionResponse.class));
}
}
public class ExecutorServiceUtil {
private static Logger log = LoggerFactory.getLogger(ExecutorServiceUtil.class);
public ExecutorService createExecuter() {
return Executors.newCachedThreadPool();
}
public void shutDownExecutor(ExecutorService executor) {
try {
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
}
catch (InterruptedException e) {
log.error("Tasks were interrupted");
}
finally {
if (!executor.isTerminated()) {
log.error("Cancel non-finished tasks");
}
executor.shutdownNow();
}
}
}
How can I use Mockito to stub the a response and return it immediately?
I've tried the below but my innovcation.args() returns [null]
PowerMockito.when(executor.submit(Matchers.<Callable<ResponseEntity<OrxPendingPostedTrxCollectionResponseV3>>> any())).thenAnswer(new Answer<FutureTask<ResponseEntity<OrxPendingPostedTrxCollectionResponseV3>>>() {
#Override
public FutureTask<ResponseEntity<OrxPendingPostedTrxCollectionResponseV3>> answer(InvocationOnMock invocation) throws Throwable {
Object [] args = invocation.getArguments();
Callable<ResponseEntity<OrxPendingPostedTrxCollectionResponseV3>> callable = (Callable<ResponseEntity<OrxPendingPostedTrxCollectionResponseV3>>) args[0];
callable.call();
return null;
}
});
You do that by not using your ExecutorServiceUtil in your test code. What I mean is: you provide a mock of that util class to your production code!
And that mock does return a "same thread executor service"; instead of a "real service" (based on a thread pool). Writing such a same-thread-executor is actually straight forward - see here.
In other words: you want two different unit tests here:
You write unit tests for your ExecutorServiceUtil class in isolation; make sure it does the thing it is supposed to do (where I think: checking that it returns a non-null ExecutorService is almost good enough!)
You write unit tests for your blah class ... that use a mocked service. And all of a sudden, all your problems around "it is async" go away; because the "async" part vanishes in thin air.
Related
I have the following methods:
#EnableAsync
#Service
Class MyService{
private String processRequest() {
log.info("Start processing request");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("Completed processing request");
return RESULT;
}
#Async
public CompletableFuture<String> getSupplyAsyncResult(){
CompletableFuture<String> future
= CompletableFuture.supplyAsync(this::processRequest);
return future;
}
#Async
public CompletableFuture<String> getCompletedFutureResult(){
CompletableFuture<String> future
= CompletableFuture.supplyAsync(this::processRequest);
return future;
}
and the following endpoints in controller:
#RequestMapping(path = "/asyncSupplyAsync", method = RequestMethod.GET)
public CompletableFuture<String> getValueAsyncUsingCompletableFuture() {
log.info("Request received");
CompletableFuture<String> completableFuture
= myService.getSupplyAsyncResult();
log.info("Servlet thread released");
return completableFuture;
}
and
#RequestMapping(path = "/asyncCompletable", method = RequestMethod.GET)
public CompletableFuture<String> getValueAsyncUsingCompletableFuture() {
log.info("Request received");
CompletableFuture<String> completableFuture
= myService.getCompletedFutureResult();
log.info("Servlet thread released");
return completableFuture;
}
Why would anyone use completableFuture.supplyAsync within #Async method in Spring endpoint?
I assume using completableFuture.completedFuture is more appropriate, please share your views.
They serve entirely different purposes to begin with. Before you think about how much it takes one or the other to process, you might want to understand how they work, first (so little calls are no indication of slow/fast anyway; these numbers mean nothing in this context).
Here is the same example you have:
public class SO64718973 {
public static void main(String[] args) {
System.out.println("dispatching to CF...");
//CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> processRequest());
CompletableFuture<String> future = CompletableFuture.completedFuture(processRequest());
System.out.println("done dispatching to CF...");
future.join();
}
private static String processRequest() {
System.out.println("Start processing request");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Completed processing request");
return "RESULT";
}
}
You can run this and then change the implementation (by uncommenting CompletableFuture.supplyAsync) and see where those System.out.println occur. You will notice that completedFuture will block main thread until it is executed, while supplyAsync will run in a different thread. So it's not like one is wrong and one is not, it depends on your use-case.
In general, it is not a great idea to use CompletableFuture.supplyAsync without configuring a pool for it; otherwise it will consume threads from ForkJoinPool.
I am building a library that needs to some bluetooth operations on Android. I want to return a Future instance, so whoever is using my library can call .get() on the future returned and can handle ExecutionException, TimeoutException and InterruptedException themselves. However, I want to detect a timeout myself because I need to some cleanup logic like disconnecting from the device and so on. How can I achieve this?
You could implement a wrapper class around Future which delegates to a different one (the one returned by wherever you're getting your Future at the moment). Something like:
final class DelegatingFuture<T> implements Future<T> {
private final Future<T> delegate;
DelegatingFuture(final Future<T> delegate) {
this.delegate = Objects.requireNonNull(delegate);
}
// All other methods simply delegate to 'delegate'
#Override
public T get()
throws InterruptedException, ExecutionException {
try {
return this.delegate.get();
} catch (final Exception ex) {
// Handle cleanup...
throw ex;
}
}
// Something similar for get(long timeout, TimeUnit unit)
}
And then simply return new DelegatingFuture<>(currentFuture); wherever your handing these out.
The timeout is relevant to the caller of the get method with timeout and only to that caller. A timeout is nowhere meant to imply a cancellation. E.g., the following code is a legitimate usage of the Future API:
ExecutorService es = Executors.newSingleThreadExecutor();
Future<String> f = es.submit(() -> {
Thread.sleep(3000);
return "hello";
});
for(;;) try {
String s = f.get(500, TimeUnit.MILLISECONDS);
System.out.println("got "+s);
break;
}
catch(TimeoutException ex) {
// perhaps, do some other work
System.out.println("will wait something more");
}
catch (ExecutionException ex) {
System.out.println("failed with "+ex);
break;
}
es.shutdown();
Tying the cleanup to the methods actually intended to query the result, is not a useful approach. The timeout provided by the caller(s) of that method do not relate to the actual operation. There’s not even a guaranty that the result will be queried before the operations ends or that it gets queried at all.
The cleanup should happen when either, the operation finished or when the future gets cancelled explicitly. If the caller intends a cancellation after a timeout, the caller only needs to invoke cancel after catching a TimeoutException.
One approach, often pointed to, is to use a CompletionService, e.g.
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
static final CompletionService<String> COMPLETION_SERVICE
= new ExecutorCompletionService<>(MY__EXECUTOR);
static final Future<?> CLEANER = MY__EXECUTOR.submit(() -> {
for(;;) try {
Future<String> completed = COMPLETION_SERVICE.take();
System.out.println("cleanup "+completed);
} catch(InterruptedException ex) {
if(MY__EXECUTOR.isShutdown()) break;
}
});
public static Future<String> doSomeWork() {
return COMPLETION_SERVICE.submit(() -> {
Thread.sleep(3000);
return "hello";
});
}
You are in control over when to poll the completed futures, like in another background thread, as shown in the example, or right before commencing new jobs.
You can test it like
Future<String> f = doSomeWork();
try {
String s = f.get(500, TimeUnit.MILLISECONDS);
System.out.println("got "+s);
}
catch(TimeoutException ex) {
System.out.println("no result after 500ms");
}
catch (ExecutionException ex) {
System.out.println("failed with "+ex);
}
if(f.cancel(true)) System.out.println("canceled");
f = doSomeWork();
// never calling get() at all
But honestly, I never understood why such complicated things are actually necessary. If you want a cleanup at the right time, you can use
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
public static Future<String> doSomeWork() {
Callable<String> actualJob = () -> {
Thread.sleep(3000);
return "hello";
};
FutureTask<String> ft = new FutureTask<>(actualJob) {
#Override
protected void done() {
System.out.println("cleanup "+this);
}
};
MY__EXECUTOR.execute(ft);
return ft;
}
to achieve the same.
Or even simpler
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
public static Future<String> doSomeWork() {
Callable<String> actualJob = () -> {
Thread.sleep(3000);
return "hello";
};
return MY__EXECUTOR.submit(() -> {
try {
return actualJob.call();
}
finally {
// perform cleanup
System.out.println("cleanup");
}
});
}
In either case, the cleanup will be performed whether the job was completed successfully, failed, or got canceled. If cancel(true) was used and the actual job supports interruption, the cleanup also will be performed immediately after.
I've got a class HttpClient that has a function that returns CompletableFuture:
public class HttpClient {
public static CompletableFuture<int> getSize() {
CompletableFuture<int> future = ClientHelper.getResults()
.thenApply((searchResults) -> {
return searchResults.size();
});
return future;
}
}
Then another function calls this function:
public class Caller {
public static void caller() throws Exception {
// some other code than can throw an exception
HttpClient.getSize()
.thenApply((count) -> {
System.out.println(count);
return count;
})
.exceptionally(ex -> {
System.out.println("Whoops! Something happened....");
});
}
}
Now, I want to write a test to simulates that ClientHelper.getResults fails, so for that I wrote this:
#Test
public void myTest() {
HttpClient mockClient = mock(HttpClient.class);
try {
Mockito.doThrow(new CompletionException(new Exception("HTTP call failed")))
.when(mockClient)
.getSize();
Caller.caller();
} catch (Exception e) {
Assert.fail("Caller should not have thrown an exception!");
}
}
This test fails. The code within exceptionally never gets executed. However, if I run the source code normally and the HTTP call does fail, it goes to the exceptionally block just fine.
How must I write the test so that the exceptionally code is executed?
I got this to work by doing this in the test:
CompletableFuture<Long> future = new CompletableFuture<>();
future.completeExceptionally(new Exception("HTTP call failed!"));
Mockito.when(mockClient.getSize())
.thenReturn(future);
Not sure if this is the best way though.
I have 2 Synchronous methods, the following one does not block.
#RequestMapping("/send1")
#Async
public Future<Boolean> sendMail() throws InterruptedException {
System.out.println("sending mail 1..-"
+ Thread.currentThread().getName());
Thread.sleep(1000 * 16);
System.out.println("sending mail 1 completed");
return new AsyncResult<Boolean>(true);
}
But the following one blocks.
#RequestMapping("/send3")
public void callAsyn3() throws InterruptedException, ExecutionException {
Future<Boolean> go = sendMail3("test");
}
#Async
public Future<Boolean> sendMail3(String msg) throws InterruptedException {
boolean acceptedYet = false;
Thread.sleep(1000 * 12);
if (!msg.equalsIgnoreCase("")) {
acceptedYet = true;
}
return new AsyncResult<>(acceptedYet);
}
They are in the same controller class, Why such different behavior?
In the second case you call internally method. so the #Async is ignored (not proxyed method called).
There are two ways to fix
The first one is to ntroduce a separate bean (e.g. MyService) and move the annotated with #Async method there.
The second way is Autowire the controller to itself
#Controller
public class MyController {
#Autowired
private MyController myController;
#RequestMapping("/send3")
public void callAsyn3() throws InterruptedException, ExecutionException {
Future<Boolean> go = myController.sendMail3("test");
}
#Async
public Future<Boolean> sendMail3(String msg) throws InterruptedException {
boolean acceptedYet = false;
Thread.sleep(1000 * 12);
if (!msg.equalsIgnoreCase("")) {
acceptedYet = true;
}
return new AsyncResult<>(acceptedYet);
}
Calling method in the same class do not pass through the proxy. So you can not use a method with #Async in the same class and make calls asynchronous.
We can create another service and write the #Async method there. Something like this
#Service
public class MyService {
#Async
public Future<Boolean> sendMail3(String msg) throws InterruptedException {
boolean acceptedYet = false;
Thread.sleep(1000 * 12);
if (!msg.equalsIgnoreCase("")) {
acceptedYet = true;
}
return new AsyncResult<>(acceptedYet);
}
}
This will run asynchronously ( non-blocking ).
If you want to do this in the same controller, you can submit it manually to some thread pool.
you have self-invocation , call method sendMail3 from method callAsyn3 directly. it doesn't work because it bypasses the proxy and calls the underlying method directly.
simple fix - you should get contoller from context and call callAsyn3 from this instance.
normal fix - create new service - asyncSendMailComponent/Service, move sendMail3 into asyncSendMailComponent , inject asyncSendMailComponent into you controller and call sendMail3
in controller :
#Autowired
private AsyncSendMailComponent asyncSendMailComponent;
#RequestMapping("/send3")
public void callAsyn3() throws InterruptedException, ExecutionException {
Future<Boolean> go = asyncSendMailComponent.sendMail3(msg)
}
Async service
#Service
pubclic class AsyncSendMailComponent {
#Async
public Future<Boolean> sendMail3(String msg) throws InterruptedException {
boolean acceptedYet = false;
Thread.sleep(1000 * 12);
if (!msg.equalsIgnoreCase("")) {
acceptedYet = true;
}
return new AsyncResult<>(acceptedYet);
}
}
I am trying to write a test for my android app that communicates with a cloud service.
Theoretically the flow for the test is supposed to be this:
Send request to the server in a worker thread
Wait for the response from the server
Check the response returned by the server
I am trying to use Espresso's IdlingResource class to accomplish that but it is not working as expected. Here's what I have so far
My Test:
#RunWith(AndroidJUnit4.class)
public class CloudManagerTest {
FirebaseOperationIdlingResource mIdlingResource;
#Before
public void setup() {
mIdlingResource = new FirebaseOperationIdlingResource();
Espresso.registerIdlingResources(mIdlingResource);
}
#Test
public void testAsyncOperation() {
Cloud.CLOUD_MANAGER.getDatabase().getCategories(new OperationResult<List<Category>>() {
#Override
public void onResult(boolean success, List<Category> result) {
mIdlingResource.onOperationEnded();
assertTrue(success);
assertNotNull(result);
}
});
mIdlingResource.onOperationStarted();
}
}
The FirebaseOperationIdlingResource
public class FirebaseOperationIdlingResource implements IdlingResource {
private boolean idleNow = true;
private ResourceCallback callback;
#Override
public String getName() {
return String.valueOf(System.currentTimeMillis());
}
public void onOperationStarted() {
idleNow = false;
}
public void onOperationEnded() {
idleNow = true;
if (callback != null) {
callback.onTransitionToIdle();
}
}
#Override
public boolean isIdleNow() {
synchronized (this) {
return idleNow;
}
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.callback = callback;
}}
When used with Espresso's view matchers the test is executed properly, the activity waits and then check the result.
However plain JUNIT4 assert methods are ignored and JUnit is not waiting for my cloud operation to complete.
Is is possible that IdlingResource only work with Espresso methods ? Or am I doing something wrong ?
I use Awaitility for something like that.
It has a very good guide, here is the basic idea:
Wherever you need to wait:
await().until(newUserIsAdded());
elsewhere:
private Callable<Boolean> newUserIsAdded() {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
return userRepository.size() == 1; // The condition that must be fulfilled
}
};
}
I think this example is pretty similar to what you're doing, so save the result of your asynchronous operation to a field, and check it in the call() method.
Junit will not wait for async tasks to complete. You can use CountDownLatch to block the thread, until you receive response from server or timeout.
Countdown latch is a simple yet elegant solution and does NOT need an external library. It also helps you focus on the actual logic to be tested rather than over-engineering the async wait or waiting for a response
void testBackgroundJob() {
Latch latch = new CountDownLatch(1);
//Do your async job
Service.doSomething(new Callback() {
#Override
public void onResponse(){
ACTUAL_RESULT = SUCCESS;
latch.countDown(); // notify the count down latch
// assertEquals(..
}
});
//Wait for api response async
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
assertEquals(expectedResult, ACTUAL_RESULT);
}