I've been looking for hints on how to best test Spring MVC Controller methods that return SseEmitters. I have come up pretty short, but have a trial-and-error solution that tests against asynchronous, threaded behavior. The below is sample code just to demonstrate concept, there may be a typo or two:
Controller Class:
#Autowired
Publisher<MyResponse> responsePublisher;
#RequestMapping("/mypath")
public SseEmitter index() throws IOException {
SseEmitter emitter = new SseEmitter();
Observable<MyResponse> responseObservable = RxReactiveStreams.toObservable(responsePublisher);
responseObservable.subscribe(
response -> {
try {
emitter.send(response);
} catch (IOException ex) {
emitter.completeWithError(ex);
}
},
error -> {
emitter.completeWithError(error);
},
emitter::complete
);
return emitter;
}
Test Class:
//A threaded dummy publisher to demonstrate async properties.
//Sends 2 responses with a 250ms pause in between.
protected static class MockPublisher implements Publisher<MyResponse> {
#Override
public void subscribe(Subscriber<? super MyResponse> subscriber) {
new Thread() {
#Override
public void run() {
try {
subscriber.onNext(response1);
Thread.sleep(250);
subscriber.onNext(response2);
} catch (InterruptedException ex) {
}
subscriber.onComplete();
}
}.start();
}
}
//Assume #Configuration that autowires the above mock publisher in the controller.
//Tests the output of the controller method.
#Test
public void testSseEmitter() throws Exception {
String path = "http://localhost/mypath/";
String expectedContent = "data:" + response1.toString() + "\n\n" +
"data:" + response2.toString() + "\n\n");
//Trial-and-Error attempts at testing this SseEmitter mechanism have yielded the following:
//- Returning an SseEmitter triggers 'asyncStarted'
//- Calling 'asyncResult' forces the test to wait for the process to complete
//- However, there is no actual 'asyncResult' to test. Instead, the content is checked for the published data.
mockMvc.perform(get(path).contentType(MediaType.ALL))
.andExpect(status().isOk())
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(nullValue()))
.andExpect(header().string("Content-Type", "text/event-stream"))
.andExpect(content().string(expectedContent))
}
As noted in the comments, asyncResult() is called to ensure that the publisher finishes its work and sends both responses before the test completes. Without it, the content check fails due to only one response being present in the content. However there is no actual result to check, hence asyncResult is null.
My specific question is whether there is a better, more precise way to force the test to wait for the async process to finish, rather than the klugie method here of waiting for a non-existent asyncResult. My broader question is whether there are other libs or Spring methods that are better suited to this vs. these async functions. Thanks!
This is a more general answer as it is meant to test an SseEmitter that will run forever, but will disconnect from SSE stream after a given timeout.
As for a different approach than MVC, as #ErinDrummond commented to the OP, you might want to investigate WebFlux.
It is a minimal example. One might want to expand with headers to the request, different matchers or maybe work on the stream output separately.
It is setting a delayed thread for disconnecting from SSE Stream which will allow to perform assertions.
#Autowired
MockMvc mockMvc;
#Test
public void testSseEmitter(){
ScheduledExecutorService execService = Executors.newScheduledThreadPool(1);
String streamUri = "/your-get-uri");
long timeout = 500L;
TimeUnit timeUnit = TimeUnit.MILLISECONDS;
MvcResult result = mockMvc.perform(get(streamURI)
.andExpect(request().asyncStarted()).andReturn();
MockAsyncContext asyncContext = (MockAsyncContext) result.getRequest().getAsyncContext();
execService.schedule(() -> {
for (AsyncListener listener : asyncContext.getListeners())
try {
listener.onTimeout(null);
} catch (IOException e) {
e.printStackTrace();
}
}, timeout, timeUnit);
result.getAsyncResult();
// assertions, e.g. response body as string contains "xyz"
mvc.perform(asyncDispatch(result)).andExpect(content().string(containsString("xyz")));
}
Related
Could somebody help me to do the following:
#PostContruct public void func() {
webclient.get()...subscribe();
}
webclient call will terminate after func() returns. Most likely it will happen before the first request comes in, but no guarantees. The other option is to block(), which defeats the purpose of being reactive.
What would be the right way to make reactive calls in #PostConstruct methods?
Thank you.
I created a simple bean.
Synchronous update:
#Component
public class BeanTest {
private String postConstructValue;
#PostConstruct
public void init(){
try {
Thread.sleep(5000);
this.postConstructValue = "Construction done";
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Scheduled(fixedRate = 500)
public void print(){
System.out.println(
this.postConstructValue
);
}
}
It took some time for app to start (more than 5 seconds) because we simulated some time consuming process in the post construct. Scheduled print method started printing only after the app started. It started printing "Construction done" message.
Asynchronous update:
#Component
public class BeanTest {
private String postConstructValue;
#PostConstruct
public void init(){
Flux.just("Construction done")
.delayElements(Duration.ofSeconds(5))
.subscribe(s -> this.postConstructValue = s);
}
#Scheduled(fixedRate = 500)
public void print(){
System.out.println(
this.postConstructValue
);
}
}
Now in this approach, the app started within 2 seconds. Print method starting printing null for few seconds. Then it started printing "Construction done". It does not terminate the Flux postConstruct value update. It happened asynchronously.
Reactive approach is good when you want a non-blocking behavior and getting something done asynchronously. If you think that your component creation should wait for proper construction, you have to block! Otherwise, you can go with second approach.
Is it possible with Project Reactor to wait in a mono for an event / condition without needing to use a blocking thread per mono? With a CompletableFuture I can pull such a thing off but I can't see how to do it with Project Reactor.
My problem is that I need to correlate requests with responses. The response time varies wildly and some will even never get a reply and timeout. When on the client side a blocking thread per request isn't a problem but since this is a server application I don't want to end up with spawning a thread per request that blocks waiting for a response.
The API looks something like this:
Mono<Response> doRequest(Mono<Request> request);
Since I don't know how to do it with Reactor I will explain how to do it with a CompletableFuture to clarify what I'm looking for. The API would look like this:
CompletableFuture<Response> doRequest(Request request);
When invoked by a caller a request to a server is made which has a correlation ID in it generated by this method. The caller is returned a CompletableFuture and the method stores a reference to this CompletableFuture in map with the correlation ID as key.
There is also a thread (pool) which receives all the responses for the server. When it receives a response it takes the correlation ID from the response and uses it to look up the original request (ie. the CompletableFuture) in the map and calls complete(response); on it.
In this implementation you don't need a blocking thread per request. This is basically more of a Vert.X / Netty way of thinking? I would like to know how to implement such a thing (if possible) with Project Reactor.
EDIT 25-07-2019:
As per request in the comments to clarify what I'm getting at below is an example of how I would implement this with CompleteableFuture's.
I also noticed I made a mistake which might have been rather confusing: In the CompletableFuture example I passed a Mono as argument. That should have been just a "normal" argument. My apologies and I hope I didn't confuse people too much with it.
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
class NonBlockingCorrelatingExample {
/**
* This example shows how to implement correlating requests with responses without needing a (sleeping)
* thread per request to wait for the response with the use of {#link CompletableFuture}'s.
*
* So the main feat of this example is that there is always a fixed (small) number of threads used even if one
* would fire a thousands requests.
*/
public static void main(String[] args) throws Exception {
RequestResponseService requestResponseService = new RequestResponseService();
Request request = new Request();
request.correlationId = 1;
request.question = "Do you speak Spanish?";
CompletableFuture<Response> responseFuture = requestResponseService.doRequest(request);
responseFuture.whenComplete((response, throwable) -> System.out.println(response.answer));
// The blocking call here is just so the application doesn't exit until the demo is completed.
responseFuture.get();
}
static class RequestResponseService {
/** The key in this map is the correlation ID. */
private final ConcurrentHashMap<Long, CompletableFuture<Response>> responses = new ConcurrentHashMap<>();
CompletableFuture<Response> doRequest(Request request) {
Response response = new Response();
response.correlationId = request.correlationId;
CompletableFuture<Response> reponseFuture = new CompletableFuture<>();
responses.put(response.correlationId, reponseFuture);
doNonBlockingFireAndForgetRequest(request);
return reponseFuture;
}
private void doNonBlockingFireAndForgetRequest(Request request) {
// In my case this is where the request would be published on an MQTT broker (message bus) in a request topic.
// Right now we will just make a call which will simulate a response message coming in after a while.
simulateResponses();
}
private void processResponse(Response response) {
// There would usually be a (small) thread pool which is subscribed to the message bus which receives messages
// in a response topic and calls this method to handle those messages.
CompletableFuture<Response> responseFuture = responses.get(response.correlationId);
responseFuture.complete(response);
}
void simulateResponses() {
// This is just to make the example work. Not part of the example.
new Thread(() -> {
try {
// Simulate a delay.
Thread.sleep(10_000);
Response response = new Response();
response.correlationId = 1;
response.answer = "Si!";
processResponse(response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
static class Request {
long correlationId;
String question;
}
static class Response {
long correlationId;
String answer;
}
}
Yes, it is possible. You can use reactor.core.publisher.Mono#create method to achieve it
For your example:
public static void main(String[] args) throws Exception {
RequestResponseService requestResponseService = new RequestResponseService();
Request request = new Request();
request.correlationId = 1;
request.question = "Do you speak Spanish?";
Mono<Request> requestMono = Mono.just(request)
.doOnNext(rq -> System.out.println(rq.question));
requestResponseService.doRequest(requestMono)
.doOnNext(response -> System.out.println(response.answer))
// The blocking call here is just so the application doesn't exit until the demo is completed.
.block();
}
static class RequestResponseService {
private final ConcurrentHashMap<Long, Consumer<Response>> responses =
new ConcurrentHashMap<>();
Mono<Response> doRequest(Mono<Request> request) {
return request.flatMap(rq -> doNonBlockingFireAndForgetRequest(rq)
.then(Mono.create(sink -> responses.put(rq.correlationId, sink::success))));
}
private Mono<Void> doNonBlockingFireAndForgetRequest(Request request) {
return Mono.fromRunnable(this::simulateResponses);
}
private void processResponse(Response response) {
responses.get(response.correlationId).accept(response);
}
void simulateResponses() {
// This is just to make the example work. Not part of the example.
new Thread(() -> {
try {
// Simulate a delay.
Thread.sleep(10_000);
Response response = new Response();
response.correlationId = 1;
response.answer = "Si!";
processResponse(response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
I have some basic project that has like four calls to some external resource, that in current version runs synchronously. What I would like to achieve is to wrap that calls into HystrixObservableCommand and then call it asynchronously.
From what I have read, after calling .observe() at the HystrixObservableCommand object, the wrapped logic should be called immediately and asynchronously. However I am doing something wrong, because it works synchronously.
In the example code, the output is Void, because I'm not interested in output (for now). That is also why I did not assigned the Observable to any object, just called constructor.observe().
#Component
public class LoggerProducer {
private static final Logger LOGGER = Logger.getLogger(LoggerProducer.class);
#Autowired
SimpMessagingTemplate template;
private void push(Iterable<Message> messages, String topic) throws Exception {
template.convertAndSend("/messages/"+topic, messages);
}
public void splitAndPush(Iterable<Message> messages) {
Map<MessageTypeEnum, List<Message>> groupByMessageType = StreamSupport.stream(messages.spliterator(), true)
.collect(Collectors.groupingBy(Message::getType));
//should be async - it's not
new CommandPushToBrowser(groupByMessageType.get(MessageTypeEnum.INFO),
MessageTypeEnum.INFO.toString().toLowerCase()).observe();
new CommandPushToBrowser(groupByMessageType.get(MessageTypeEnum.WARN),
MessageTypeEnum.WARN.toString().toLowerCase()).observe();
new CommandPushToBrowser(groupByMessageType.get(MessageTypeEnum.ERROR),
MessageTypeEnum.ERROR.toString().toLowerCase()).observe();
}
class CommandPushToBrowser extends HystrixObservableCommand<Void> {
private Iterable<Message> messages;
private String messageTypeName;
public CommandPushToBrowser(Iterable<Message> messages, String messageTypeName) {
super(HystrixCommandGroupKey.Factory.asKey("Messages"));
this.messageTypeName = messageTypeName;
this.messages = messages;
}
#Override
protected Observable<Void> construct() {
return Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<? super Void> observer) {
try {
for (int i = 0 ; i < 50 ; i ++ ) {
LOGGER.info("Count: " + i + " messageType " + messageTypeName);
}
if (null != messages) {
push(messages, messageTypeName);
LOGGER.info("Message type: " + messageTypeName + " pushed: " + messages);
}
if (!observer.isUnsubscribed()) {
observer.onCompleted();
}
} catch (Exception e) {
e.printStackTrace();
observer.onError(e);
}
}
});
}
}
}
There are some pure "test" code fragments there, as I was trying to figure out the problem, just ignore the logic, main focus is to make it run async with .observe(). I do know that I may achieve that with standard HystrixCommand, but this is not the goal.
Hope someone helps :)
Regards,
Answer was found:
"Observables do not add concurrency automatically. If you are modeling
synchronous, blocking execution with an Observable, then they will
execute synchronously.
You can easily make it asynchronous by scheduling on a thread using
subscribeOn(Schedulers.io()). Here is a simply example for wrapping a
blocking call with an Observable:
https://speakerdeck.com/benjchristensen/applying-reactive-programming-with-rxjava-at-goto-chicago-2015?slide=33
However, if you are wrapping blocking calls, you should just stick
with using HystrixCommand as that’s what it’s built for and it
defaults to running everything in a separate thread. Using
HystrixCommand.observe() will give you the concurrent, async
composition you’re looking for.
HystrixObservableCommand is intended for wrapping around async,
non-blocking Observables that don’t need extra threads."
-- Ben Christensen - Netflix Edge Engineering
Source: https://groups.google.com/forum/#!topic/hystrixoss/g7ZLIudE8Rs
I have a test of communication over a TCP socket where I expect the server to not respond within a set time frame when I send a certain message.
The servers behaviour is nothing I can control.
I know how to fail a test if it has not completed within a set time frame. But how can I do the opposite, make it pass for not completing within the timeframe?
I can use #Test (timeout=1000) to make a test fail if not complete within a second.
But, using Junit 4, is there a function to test for an expected timeout as a positive result? I.e. The test will fail if completed within the time frame and pass if not?
Good question. Actually you can do this using only junit tools. My idea is to inverse Timeout rule behaviour + use expected attribute of Test annotation. The only one limitation: you have to place your test in separate class, because Rule applies to all tests inside it:
public class Q37355035 {
private static final int MIN_TIMEOUT = 100;
#Rule
public Timeout timeout = new Timeout(MIN_TIMEOUT) {
public Statement apply(Statement base, Description description) {
return new FailOnTimeout(base, MIN_TIMEOUT) {
#Override
public void evaluate() throws Throwable {
try {
super.evaluate();
throw new TimeoutException();
} catch (Exception e) {}
}
};
}
};
#Test(expected = TimeoutException.class)
public void givesTimeout() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
}
}
I'm building upon the great answer by Andremoniy, don't forget to upvote his answer if you like this one!
I used the following modification to skip a test if it doesn't finish in the expected time frame. The benefit of this is that the test will be marked as Skipped by JUnit instead of Successful. This is good for optimistic execution of tests which can sometimes hang or not finish fast enough, but you don't want to mark them as failed or delete them.
public class OptimisticTestClass {
private static final int TEST_METHOD_TIMEOUT_MS = 100;
#Rule
public Timeout timeout = new Timeout(TEST_METHOD_TIMEOUT_MS, TimeUnit.MILLISECONDS) {
public Statement apply(Statement base, Description description) {
return new FailOnTimeout(base, TEST_METHOD_TIMEOUT_MS) {
#Override
public void evaluate() throws Throwable {
try {
super.evaluate();
} catch (TestTimedOutException e) {
Assume.assumeNoException("Test did not finish in the allocated time, skipping!", e);
}
}
};
}
};
// The test times out and is skipped
public void givesTimeout() throws InterruptedException {
Thread.sleep(1000);
}
}
It's simpler in Java 9
CompletableFuture.supplyAsync(() -> dormammu.bargain())
.orTimeout(1, TimeUnit.SECONDS)
.handle((result, throwable) -> {
if (!(throwable instanceof TimeoutException)) {
Assertions.fail();
}
return result;
}).get();
If the method doesn't return within 1 second, it will time out. In handle method you can make sure TimeoutException was thrown, otherwise fail the test.
So in the following code, when I do yService.performAction() it does it for many items and is taking too much time.
I have been advised to try to use threads to make this faster so I went online and looked and found that it's not advisable to spawn new child threads from the controller as it might mess up the Java EE container thread pool. If my yService.performAction() is taking time to run and need to make it fast, what options do I have?
Many people have been suggesting to use Quartz to spawn new threads. I am not sure if I want to do something that complex. How can I run that specific service in separate threads? Is there any simple way to do it?
#Controller
#RequestMapping("/test")
public class TestController {
private static final Logger logger = Logger.getLogger(TestController.class);
#Autowired
XService xService;
#Autowired
private YService yService;
#RequestMapping(method = { RequestMethod.GET })
public void testCheck(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
List<SL> somelist = XService.getAllX();
StringBuffer status = new StringBuffer("SUCCESS\n\n");
for (SL sl : somelist) {
boolean flag = false;
try {
flag = yService.performAction(sl);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write(status.toString());
} catch (Exception e) {
logger.error(e.getMessage(), e);
response.getWriter().write("FAILED");
}
}
}
Your best option is to use an ExecutorService and call invokeAll(). You can retrieve an instance by calling Executors.newFixedThreadPool().