Java reactor how to properly start async cancellable sideeffect - java

I'm trying to write something using reactor which I know how to write using completable futures. I'm getting "Calling subscribe in non-blocking scope" warning in it.
My goal is to call turnOn() with a timeout which should call turnOff() after the timeout. If turnOn() is called again it should cancel the old timeout and wait for a new timeout.
How should I do this? I could do a hibrate and use CompletableFuture for the timeout but reactor's api is just a bit easier.
this test works as expected:
public class TimeoutTest {
Service service;
#BeforeEach
public void setUp() {
service = mock(Service.class);
}
CompletableFuture<Void> turnOffFuture = null;
#DisplayName("Should timeout on turnOn with timeout")
#Test
public void timeoutCompletableFuture() throws InterruptedException {
turnOn(Duration.ofMillis(100)).join();
verify(service).turnOn();
verify(service,never()).turnOff();
Thread.sleep(1000);
verify(service).turnOff();
}
private interface Service{
void turnOn();
void turnOff();
}
public void cancelTimeout() {
if (turnOffFuture != null)
turnOffFuture.cancel(false);
turnOffFuture = null;
}
public CompletableFuture<Void> turnOn(Duration timeout) {
CompletableFuture<Void> turnOnFuture = turnOn();
cancelTimeout();
turnOffFuture = turnOnFuture.thenRun(() -> delay(timeout))
.thenRun(this::turnOff);
return turnOnFuture;
}
private void delay(Duration duration) {
try {
Thread.sleep(BigDecimal.valueOf(duration.getSeconds())
.scaleByPowerOfTen(3)
.add(BigDecimal.valueOf(duration.getNano(), 6))
.intValue());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private CompletableFuture<Void> turnOn() {
return CompletableFuture.runAsync(() -> service.turnOn());
}
private CompletableFuture<Void> turnOff() {
return CompletableFuture.runAsync(() -> service.turnOff());
}
}
but my reactor code does not.
public class TimeoutMonoTest {
Service service;
#BeforeEach
public void setUp() {
service = mock(Service.class);
}
Disposable turnOffDisposable = null;
#DisplayName("Should timeout on turnOn with timeout")
#Test
public void timeoutMono() throws InterruptedException {
turnOn(Duration.ofMillis(100)).block(Duration.ofMillis(10));
verify(service).turnOn();
verify(service, never()).turnOff();
Thread.sleep(1000);
verify(service).turnOff();
}
private interface Service {
void turnOn();
void turnOff();
}
public void cancelTimeout() {
if (turnOffDisposable != null)
turnOffDisposable.dispose();
turnOffDisposable = null;
}
public Mono<Void> turnOn(Duration timeout) {
Mono<Void> turnOnFuture = turnOn();
cancelTimeout();
turnOffDisposable = turnOnFuture.delayElement(timeout)
.subscribe(it -> this.turnOff());
return turnOnFuture;
}
private Mono<Void> turnOn() {
service.turnOn();
return Mono.just("not empty but mapped to void").then();
}
private Mono<Void> turnOff() {
service.turnOff();
return Mono.just("not empty but mapped to void").then();
}
}

The problem lies in the mapping to void mono's in the turnOn() and turnOff() methods. They do not actually get a "next" signal, just a "success" signal.
The fix is simply to change the turnOn method to:
public Mono<Void> turnOn(Duration timeout) {
cancelTimeout();
Mono<Void> turnOnMono = turnOn();
turnOffDisposable = turnOnMono.delayElement(timeout)
.then(turnOff())
.subscribe();
return turnOn();
}

Related

Spring WebFlux. How to add specific logic after controller call?

I have the next RestController:
#PostMapping("/play")
public Mono<PlayResponse> play(#RequestBody String body) {
//business logic here;
}
And I wanna add some additional logic after the controller call. For example, I want to add to my application some modes logic:
timeout mode - after the successful call, the response will wait until timeout happens
long answer mode - after the successful call, the response will wait a particular amount of ms
failure mode - after the successful call, the response will answer with FORBIDDEN code
etc.
I'm trying to achieve that through WebFilter:
#Component
public class OutgoingFilter implements WebFilter {
Mode mode = new TimeoutMode();
#NonNull
#Override
public Mono<Void> filter(#NonNull final ServerWebExchange exchange, final WebFilterChain chain) {
return chain.filter(exchange)
.doOnNext(this::onNext)
.map(this::onMap)
.doFinally(this::onFinally);
}
private Void onMap(final Void unused) {
mode.run();
return unused;
}
private void onNext(final Void unused) {
mode.run();
}
private void onFinally(final SignalType signalType) {
mode.run();
}
}
As you can see, I've tried onMap, doOnNext andonFinally methods and none of them seem not working.
Is it the right way to use WebFilter there? Maybe I'm doing something wrong?
How can I implement such logic in the Spring WebFlux application?
Update
There is my Mode interface:
public interface Mode {
void run() throws Forbidden;
}
Implementations:
public class Fail implements Mode {
#Override
public void run() throws Forbidden {
throw new Forbidden("Fail mode enabled");
}
}
public class Wait implements Mode {
private final int ms;
public Wait() {
this(0);
}
public Wait(final int ms) {
this.ms = ms;
}
#Override
public void run() throws Forbidden {
sleep();
}
private void sleep() {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
But the implementations aren't final. I will change them to a reactive style if it's needed.

Catch Java exception from Runnable in Unit test

I have the below code
public final class JoinableTaskPool<T> extends ABC {
private int taskCounter;
private final Object monitor;
private final ExtendedThreadPoolExecutor service;
private final CompletionService<T> compService;
public Future<T> submit(final Callable<T> task) {
final Future<T> result = compService.submit(task);
service.submit(new Runnable() {
public void run() {
try {
final Future<T> result = compService.take();
try {
handler.processResult(result);
} catch (final Throwable t) {
throw new SearchException("Task has an error", t);
}
} catch (InterruptedException e) {
throw new SearchException("Task has an error", e);
}
}
}
return result;
}
public void join() {
synchronized (monitor) {
while (taskCounter != 0) {
try {
monitor.wait();
} catch (InterruptedException e) {
error(e, "Interrupted in join");
}
}
}
}
The ExtendedThreadPoolExecutor class is defined as follows
public class ExtendedThreadPoolExecutor extends ThreadPoolExecutor {
public ExtendedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
#Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if(t != null) {
throw new SearchException("Error while executing the task", t);
}
}
}
I am trying to write a unit test for this method. Below is the method
#RunWith(MockitoJUnitRunner.class)
public class TestJoinableTaskPool<T> {
private JoinableTaskPool<T> pool;
#Before
public void setUp() {
pool = new JoinableTaskPool<T>(1);
}
#After
public void tearDown() {
pool.shutdown();
}
#Test (expected = SearchException.class)
public void testSubmit() throws Exception{
Callable task = (Callable<T>) () -> null;
Mockito.when(pool.getFinishHandler().processResult(result))
.thenThrow(RuntimeException.class);
pool.submit(task);
}
}
Since the SearchException exception is thrown in runnable, there is no way to access it outside the submit method. If I would have returned the Future returned by executorService.submit, I could have done a future.get() to get an exception, but I am returning another future (result variable).
So while writing the unit test I am not able to get the exception thrown.
Also I have overriden the afterExecute() in an effort to catch exception from unit test,but couldnt find a way to call it.
How do I test this exception thrown from the runnable from a unit test.
Any help would be appreciated.
Putting aside that his code smells a mile away, what you can do is to create additional interface eg
public interface MyErrorHandler {
handleError(Exception e)
implement it in your executor pools and call it on exception. Then you can use use Mockito.spy to see if tha method have been called or not on MyErrorHandler (cglib should allow you to do that even withount additional intrerface.)
alternatively you can define that MyExceptionHandler instance should be passed to an executor (via constructor or setter) so you will be able to provide use case dependent implementation of such callback

Not able to process request after multiple thread creation

Created a static ExecutorService object and used it to execute an async task with input in a spring boot rest controller. not able to process any request after invoking async task for 3 times.
TaskManager.java
public class TaskManager {
static ExecutorService e = Executors.newFixedThreadPool(4);
private TaskManager(){
}
public static void addAsyncTask(AsyncTaskWrapper task){
e.submit(task);
}
}
public class AsyncTaskWrapper implements Callable {
Object input,output;
Callable callback;
AsyncTask task;
public Object getInput() {
return input;
}
public void setInput(Object input) {
this.input = input;
}
public Object getOutput() {
return output;
}
public AsyncTask getTask() {
return task;
}
public void setTask(AsyncTask task) {
this.task = task;
}
public Callable getCallback() {
return callback;
}
public void setCallback(Callable callback) {
this.callback = callback;
}
public AsyncTaskWrapper(AsyncTask task, Callable callback){
this.callback = callback;
this.task = task;
}
#Override
public Object call() throws Exception {
System.out.println("Started");
output = task.execute(input);
callback.call();
System.out.println("call completed");
return output;
}
}
RESTControllers.java - need to execute a task in async with input for a RESTEndpoint.
AsyncTaskWrapper task = new AsyncTaskWrapper(
(list) -> {
List l = (List) list;
try {
MailingUtils.sendRegistrationMail((String) l.get(0), (String) l.get(1));
} catch (MessagingException e) {
e.printStackTrace();
}
return null;
},
() -> {
System.out.println("Done");
return null;
});
task.setInput(objects);
TaskManager.addAsyncTask(task);
Thanks in advance

Best approach for verifying a FutureCallback effect with Jmockit

What is the preferred mechanism for verifying the effects of a callback in Jmockit?
For example, assume I have this class.
class ResultGenerator {
AsyncLauncher asyncLauncher = new AsyncLauncher();
public void getResultAsync(final ResultSignal resultSignal) {
asyncLauncher.getResult(new FutureCallback<Result>() {
#Override
public void onSuccess(#Nullable Result result) {
resultSignal.success(result);
}
#Override
public void onFailure(Throwable t) {
resultSignal.failure();
}
});
}
}
How do I verify resultSignal.success(result) when writing a test for ResultGenerator#getResultAsync?
For example
#RunWith(JMockit.class)
public class ResultGeneratorTest {
// Synchronous invocation, mocked AsyncLauncher
#Test
public void testGetResultAsync(#Mocked final ResultSignal resultSignal, #Mocked final Result result) throws Exception {
new MockUp<AsyncLauncher>() {
#Mock
void getResult(FutureCallback<Result> futureCallback) {
futureCallback.onSuccess(result);
}
};
ResultGenerator resultGenerator = new ResultGenerator();
resultGenerator.getResultAsync(resultSignal);
new Verifications() {{
resultSignal.success((Result) any); times = 1;
resultSignal.failure(); times = 0;
}};
}
// Asynchronous invocation, real AsyncLauncher in use
#Test
public void testGetResultAsyncDelayed(#Mocked final Result result) throws Exception {
final AtomicBoolean latch = new AtomicBoolean(false);
MockUp<ResultSignal> resultSignalMockUp = new MockUp<ResultSignal>() {
#Mock(invocations = 1)
public void success(Result result) {
latch.set(true);
}
#Mock(invocations = 0)
public void failure() {
latch.set(true);
}
};
ResultGenerator resultGenerator = new ResultGenerator();
final ResultSignal resultSignal = resultSignalMockUp.getMockInstance();
resultGenerator.getResultAsync(resultSignal);
Awaitility.await().untilTrue(latch);
}
}
Couple of notes:
ResultGenerator is your SUT (System Under Test) and you should not mock internals
ResultSignal is a test collaborator, so it is natural to mock it out
because you can verify the functionality as such, the only "correct" solution from unit testing theory is to mock out the collaborator
You have to be sure that you handle timeout correctly, otherwise the test might never end
so one possible solution is:
#Test
public void getResultAsync_ShouldNotifyResultSignal() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
ResultGenerator generator = new ResultGenerator();
generator.getResultAsync(new MyResultSignal(latch));
assertTrue(latch.await(1, SECONDS));
}
private static final class MyResultSignal implements ResultSignal {
private final CountDownLatch latch;
private MyResultSignal(CountDownLatch latch) {
this.latch = latch;
}
#Override
public void success(Result result) {
latch.countDown();
}
#Override
public void failure() {}
}

How to test code that implements threads?

I want to be able to junit test the following:
private ExecutorService executor = Executors.newSingleThreadExecutor();
public void foo() {
final EntityManagerFactory entityManagerFactory =
entityManager.getEntityManagerFactory();
executor.execute(new Runnable() {
#Override
public void run() {
EntityManager entityManager =
entityManagerFactory.createEntityManager();
Session session = (Session) entityManager.getDelegate();
try {
SQLQuery query =
session.createSQLQuery("SELECT * from foo_function()");
} catch (HibernateException exception) {
LOGGER.error("Exception: " + exception);
}
}
});
}
I am using Mockito. I tried to get it to throw an exception, for example:
Mockito.doThrow(SQLException.class).when(mockSession).createSQLQuery(any(String.class));
It would throw an exception in the spawned thread, but it doesn't fail when I run it as a Junit test.
You'll want to replace the Executor or ExecutorService, either in the class under test:
public class YourClass {
private Executor executorService = Executor.newSingleThreadExecutor();
public YourClass() {}
/** package-visible for testing */
YourClass(Executor executor) {
/* ... */
}
public void foo() { /* ... */ }
}
...or just in the method you're testing:
public class YourClass {
public void foo() {
foo(executorService);
}
/** package-visible for testing */
void foo(Executor executor) {
/* ... */
}
}
After that, you can either use a manual or Guava-based direct executor, as in this SO question and its answers:
#Test public void fooShouldHandlException() {
// ...
systemUnderTest.foo(new Executor() {
#Override public void execute(Runnable r) { r.run(); }
});
// ...
}
Or replace it with a Mockito mock to test both before and after the Runnable is run:
#Test public void fooShouldHandlException() {
Executor mockExecutor = Mockito.mock(Executor.class);
ArgumentCaptor<Runnable> runnableCaptor =
ArgumentCaptor.forClass(Runnable.class);
systemUnderTest.foo(mockExecutor);
// assert "before Runnable is run" state, if applicable
verify(mockExecutor).execute(runnableCaptor.capture());
runnableCaptor.getValue().run();
// assert "after Runnable is run" state
}

Categories