Spring Cloud Stream - Output Messages from EventListener - spring-boot

I'm trying to utilize the #DomainEvents mechanism provided by Spring Data to publish Events with Spring Cloud Stream (Spring Boot 2.0.0.M7 and Finchley.M5). I have a two-part question.
Why does SendTo not work on EventListeners?
Is there a better way to accomplish this?
The DomainEvent is being created and sent to the EventListener without issues. The problem is that the SendTo mechanism didn't seem to be working. The first method below would trigger, but not forward the message. Manually building the Message and sending it as shown in the second method works correctly.
#Async
#TransactionalEventListener
#SendTo(Sink.Output)
StreamedEvent handleEventWithSendTo(MyEvent event) {
// handle and create event
}
#TransactionalEventListener
void handleEvent(MyEvent event) {
// handle and create event
sink.output().send(MessageBuilder.withPayload(payload).build())
}
The call-out in the Spring Cloud Stream docs shows using SendTo on a StreamListener, which is not quite the same thing as an EventListener, but I thought it may work.
For the second part, using DomainEvents requires the service code persisting the Entity to know about the event (to either call registerEvent directly or some method on the Entity which represents the event). I was curious if using the Spring Data callback hooks (e.g. PreUpdate, PostUpdate) would be better. Or if there was a better way all together.

Related

Integration Test a Reactive Spring Cloud Stream

TLDR; How do you test a Reactive Function composition using the Test Binder?
I have a Spring Cloud Stream that uses Reactive Functions and I don't know how to test it. I don't see any official docs on how to do an Integration Test from input source to output destination binder.
In my specific case, I am connecting a Spring Integration flow using a Reactive Supplier and the IntegrationReactiveUtils.messageChannelToFlux() pattern. This works in a development environment - I can pull messages from RabbitMQ using the Spring Integration Flow and they enter the SCSt.
My SCSt has several function chained together, each one is reactive. They are composed like func1|func2|func3. I verified this works with a dev Rabbit (source) and Kafka (Destination).
I can't seem to figure out how to test this, and there doesn't seem to be any official documentation on testing a complete reactive stream. Right now I have code that roughly looks like this:
#Autowired
MessageChannel inputChannel;
#Autowired
private OutputDestination output;
#Test
void myTest() {
//omitted prep of var 'messageToSend'
this.inputChannel.send(messageToSend);
var outputMessage = output.receive(5000);
Assertions.assertNotNull(outputMessage.getPayload());
}
The error I receive is that output.receive(5000) returns null. I suspect a threading issue because I am not subscribing to the Flux and waiting for completion.
I have run a debugger in the Flux functions and see the message going all the way to the end with no errors or weirdness.
I figured this out actually. I had to specify the binder name. I had a test property spring.cloud.stream.bindings.processingStream set, which I thought made 2 new bindings (processingStream-in-0 and processingStream-out-0).
It turns out I had to set the binding name in the test code like output.receive(5000, "processingStream"), without the -out-0 suffix. I can now receive messages from the stream.

TransactionalEventListener won't kick in

I have a problem with #TransactionalEventListener.
Given:
Kotlin 1.3.50
Spring Webflux
Spring Data
Hibernate 5
Postgres JDBC (not RDBC!)
What happens:
Request to controller was made.
Service did some job within method marked as #Transactional an event and published event.
Response returned to client
Event listener marked as #Async, #TransactionalEventListener and #Transactional doesn't kick in.
What I did:
I have debugged into service method which publishes an event to find out whether an actual transaction is in progress with the help of some static utility method I don't remember, which told me what I suspected - it is in progress.
Then I enabled debug output to see log saying: No transaction is in progress. Discarding event.
I mean, both publishing and consuming sides do have annotation #Transactional on them. No alterations to default params of annotations were made.
I have found similar situation in this question question but no one answered that.
Also, I have read an article at dzone but there seems to be no mentioning of my case.
Workaround:
I can avoid this by either replacing #TransactionalEventListener to #EventListener (alas, remove transaction bound processing) or by changing param fallbackExecute to true which effectively is the same as first solution.
Suspected parties:
Possibly absence of reactive transaction manager in an webflux environment could be the case? And some weird bug not accounting JpaTransactionManager?

Spring websocket: how intercept the send event through the AbstractSubProtocolEvent hierarchy?

About Spring websocket about intercept Stomp events one approach is extends from the ChannelInterceptorAdapter class
It works how is expected. More details here:
Spring websocket: how convert easily the Message's payload to POJO in ChannelInterceptorAdapter
Now, according with this tutorial:
Detecting WebSocket Connects and Disconnects in Spring 4
the approach mentioned above is covered and other approach is work around with ApplicationEvents, it through with:
SessionConnectEvent
SessionConnectedEvent
SessionDisconnectEvent
These classes extends from:
AbstractSubProtocolEvent
And exists two subclasses more:
SessionSubscribeEvent
SessionUnsubscribeEvent
This list is confirmed in (by the same author):
how to capture connection event in my webSocket server with Spring 4?
The names are by themselves obvious to know what each one does, but just curious
Why there is no none for the send event?
It to react from:
stompClient.send('/app/ws/something',
{},
JSON.stringify({'content': $('#content').val()})
For this send event "seems" mandatory work with the ChannelInterceptorAdapter through the postSend(Message<?> message, MessageChannel channel, boolean sent) method yet
The events reflect major points in the lifecycle of a STOMP connection. They're not meant to be notifications for every message sent from the client. For that you can use #MessageMapping methods, or a ChannelInterceptor.

Spring Cloud Bus - Custom Event Received but not handled by Event Handler

I am using Spring Cloud Bus (1.2.1.RELEASE, Camden.SR2). I have a custom event (call it CustomEvent) that I have created and registered via #RemoteApplicationEventScan as well as a custom AbstractBusEndpoint implementation to publish the event. I am able to publish events to the bus just fine.
My remote application receives the event and acknowledges (I verified this using the trace endpoint). I have verified via debugging that the CustomEvent is published via the ApplicationEventPublisher provided in BusAutoConfiguration#acceptRemote. I have a bean with an event handler in my remote application (I have this auto-configured into all of my micro-services):
#Component
public class EventListener {
#EventHandler(CustomEvent.class)
public void handleCustomEvent(CustomEvent event) {
..
}
}
Despite this, the event handler will not receive the event. I traced within the AbstractApplicationEventMulticaster class and despite my bean being created (verified via beans endpoint), the listener is not in the list of ApplicationListeners retrieved by ListenerRetriever. I do see the BusAutoConfiguration.acceptLocal listener in this list, but not my custom listener.
This event handler is also registered on the source application (the one I am accessing the bus endpoint from to initiate the event). The custom listener receives the event in this application, but not the remote application.
Essentially, both applications are configured the exact same except one is configured to send the CustomEvent using an implementation of AbstractBusEndpoint.
I am out of ideas of where else to look within the Spring code to debug this problem. If anyone has a thread they can lead me on it would be appreciated.
I've come up with the exact same problem, and debugging it revealed that ApplicationListener which handles the custom remote event not returned among candidate listeners within ApplicationEventMulticaster due to eventType was loaded by two different classloaders, one of them was devtools related classloader. Removing devtools dependency from classpath simply resolved issue for me.

spring deferredresult

I am new to spring and want to implement long polling for a website to display admin message immediately when it becomes available to all clients,i searched google for hours and could only find out deferredresult(spring 3.2) can be used to implement it.my question is how i can achieve long polling with deferredresult, I would appreciate it if anyone could refer me to such a tutorial.
Another option is to use AsyncContext. This will keep the initial GET request "open" and enable you to send multiple messages as part of the response, unlike DeferredResult which allows to send only ONE response message. Here is a good-link that explains how !
Straight from the horses mouth.
You have two basic options: Option 1 is a Callable
, where the Callable returns the String view name (you may also be able to use #ResponseBody or some of the other normal Spring return types like ModelAndView, but I have never investigated that).
Option two is to return DeferredResult, which is like Callable. except you can pass that off to a separate thread and fill in the results there. Again, not sure if you can return a ModelAndView or use #ResponseBody to return XML/JSON, but I am sure you can.
Short background about DeferredResult:
Your controller is eventually a function executed by the servlet container (for that matter, let's assume that the server container is Tomcat) worker thread. Your service flow start with Tomcat and ends with Tomcat. Tomcat gets the request from the client, holds the connection, and eventually returns a response to the client. Your code (controller or servlet) is somewhere in the middle.
Consider this flow:
Tomcat get client request.
Tomcat executes your controller.
Release Tomcat thread but keep the client connection (don't return response) and run heavy processing on different thread.
When your heavy processing complete, update Tomcat with its response and return it to the client (by Tomcat).
Because the servlet (your code) and the servlet container (Tomcat) are different entities, then to allow this flow (releasing tomcat thread but keep the client connection) we need to have this support in their contract, the package javax.servlet, which introduced in Servlet 3.0 . Spring MVC use this new Servlet 3.0 capability when the return value of the controller is DeferredResult or Callable, although they are two different things. Callable is an interface that is part of java.util, and it is an improvement for the Runnable interface. DeferredResult is a class designed by Spring to allow more options (that I will describe) for asynchronous request processing in Spring MVC, and this class just holds the result (as implied by its name) while your Callable implementation holds the async code. So it means you can use both in your controller, run your async code with Callable and set the result in DeferredResult, which will be the controller return value. So what do you get by using DeferredResult as the return value instead of Callable? DeferredResult has built-in callbacks like onError, onTimeout, and onCompletion. It makes error handling very easy. In addition, as it is just the result container, you can choose any thread (or thread pool) to run on your async code. With Callable, you don't have this choice.
Here you can find a simple working examples I created with both options, Callable and DeferredResult.

Resources