#EventListener(ApplicationReadyEvent.class) starts only one method? - spring

I'm trying to run a few methods after the Spring Boot project starts. I'm using #EventListener(ApplicationReadyEvent.class) annotation above the methods I want ran after the project launches. But it's only starting for one method at a time. I want multiple methods started at once. Is that the expected behavior for #EventListener(ApplicationReadyEvent.class)?

Its OK to place several (more than one) methods annotated with #EventListener all of them will be executed:
#Configuration
public class SampleConfiguration {
#Bean
public SampleBean sampleBean() {return new SampleBean();}
#EventListener
public void onApplicationReadyEvent(ApplicationReadyEvent event) {
System.out.println("Hello");
}
#EventListener
public void onApplicationReadyEvent2(ApplicationReadyEvent event) {
System.out.println("How are you");
}
}
This will print both "Hello" and "How are you" upon the succesfull start of the Application Context.
Now, its true that spring doesn't invoke them concurrently, it resolves all the listeners and calls them sequentially.
If you need a parallel execution you can create one listener that will be an "entry point" for the logical tasks that must be run in parallel and use Threads / Thread Pool Executors to run the code of your choice in parallel

Did you try adding #Async also above the method?
This listener is invoked synchronously. You can make it asynchronous by simply adding #Async annotation.

You can have the event listeners execute asynchronously by adding the following bean to your #Configuration class.
#Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster =
new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return eventMulticaster;
}
If you've defined a custom TaskExecutor then you should replace new SimpleAsyncTaskExecutor() with yourCustomTaskExecutorBeanMethod()

I had run into a similar issue where ApplicationReadyEvent was annoted for 3 function but in debugging we found it to fire for only 1 always. We added #Order and kept that function with the highest index(i.e. lowest priority), as in our case it was running for an indefinite while loop, and found that all the 3 functions were invoked seamlessly.

Related

What's the "Right Way" to send a data changed websocket event and ensure the database is committed in Spring Boot

Note: read the end of the answer for the way I implemented #Nonika's suggestions
What's the "right way" to send a websocket event on data insert?
I'm using a Spring Boot server with SQL/JPA and non-stomp websockets. I need to use "plain" websockets as I'm using Java clients where (AFAIK) there's no stomp support.
When I make a change to the database I need to send the event to the client so I ended up with an implementation like this:
#Transactional
public void addEntity(...) {
performActualEntityAdding();
sendEntityAddedEvent(eventData);
}
#Transactional
public void sendEntityAddedEvent(String eventData) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
sendEntityAddedEventAsync(eventData);
}
});
}
#Async
public void sendEntityAddedEventAsync(String eventData) {
// does the websocket session sending...
}
This works. If I would just call the sendEntityAddedEventAsync it would also work for real world scenarios but it fails on unit tests because the event would arrive before transaction commit. As such when the unit test invokes a list of the entities after the event it fails.
This feels like a hack that shouldn't be here. Is there a better way to ensure a commit?
I tried multiple alternative approaches and the problem is that they often worked for 10 runs of the unit tests yet failed every once in a while. That isn't acceptable.
I tried multiple approaches to solve this such as different transaction annotations and splitting the method to accommodate them. E.g read uncommitted, not supported (to force a commit) etc. Nothing worked for all cases and I couldn't find an authoritative answer for this (probably common) use case that wasn't about STOMP (which is pretty different).
Edit
One of my original attempts looked something like this:
// this shouldn't be in a transaction
public void addEntity(...) {
performActualEntityAdding();
sendEntityAddedEvent(eventData);
}
#Transactional
public void performActualEntityAdding(...) {
//....
}
#Async
public void sendEntityAddedEventAsync(String eventData) {
// does the websocket session sending...
}
The assumption here is that when sendEntityAddedEventAsync is invoked the data would already be in the database. It wasn't for a couple of additional milliseconds.
A few additional details:
Test environment is based on h2 (initially I mistakenly wrote hsql)
Project is generated by JHipster
Level 2 cache is used but disabled as NONE for these entities
Solution (based on #Nonika's answer):
The solution for me included something similar to this:
public class WebEvent extends ApplicationEvent {
private ServerEventDAO event;
public WebEvent(Object source, ServerEventDAO event) {
super(source);
this.event = event;
}
public ServerEventDAO getEvent() {
return event;
}
}
#Transactional
public void addEntity(...) {
performActualEntityAdding();
applicationEventPublisher.publishEvent(new WebEvent(this, evtDao));
}
#Async
#TransactionalEventListener
public void sendEntityAddedEventAsync(WebEvent eventData) {
// does the websocket session sending...
}
This effectively guarantees that the data is committed properly before sending the event and it runs asynchronously to boot. Very nice and simple.
Spring is using AdviceMode.PROXY for both #Async and #Transactional this is quote from the javadoc:
The default is AdviceMode.PROXY. Please note that proxy mode allows
for interception of calls through the proxy only. Local calls within
the same class cannot get intercepted that way; an Async annotation on
such a method within a local call will be ignored since Spring's
interceptor does not even kick in for such a runtime scenario. For a
more advanced mode of interception, consider switching this to
AdviceMode.ASPECTJ.
This rule is common for almost all spring annotations which requires proxy to operate.
Into your first example, you have a #Transactional annotation on both addEntity(..) and performActualEntityAdding(..). I suppose you call addEntity from another class so #Transactional works as expected. process in this scenario can be described in this flow
// -> N1 transaction starts
addEntity(){
performActualEntityAdding()//-> we are still in transaction N1
sendEntityAddedEvent() // -> call to this #Async is a class local call, so this advice is ignored. But if this was an async call this would not work either.
}
//N1 transaction commits;
That's why the test fails. it gets an event that there is a change into the db, but there is nothing because the transaction has not been committed yet.
Scenario 2.
When you don't have a #Transactional addEntity(..) then second transaction for performActualEntityAdding not starts as there is a local call too.
Options:
You can use some middleware class to call these methods to trigger
spring interceptors.
you can use Self injection with Spring
if you have Spring 5.0 there is handy #TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)

Reset state before each Spring scheduled (#Scheduled) run

I have a Spring Boot Batch application that needs to run daily. It reads a daily file, does some processing on its data, and writes the processed data to a database. Along the way, the application holds some state such as the file to be read (stored in the FlatFileItemReader and JobParameters), the current date and time of the run, some file data for comparison between read items, etc.
One option for scheduling is to use Spring's #Scheduled such as:
#Scheduled(cron = "${schedule}")
public void runJob() throws Exception {
jobRunner.runJob(); //runs the batch job by calling jobLauncher.run(job, jobParameters);
}
The problem here is that the state is maintained between runs. So, I have to update the file to be read, the current date and time of the run, clear the cached file data, etc.
Another option is to run the application via a unix cron job. This will obviously meet the need to clear state between runs but I prefer to tie the job scheduling to the application instead of the OS (and prefer it to OS agnostic). Can the application state be reset between #Scheduled runs?
You could always move the code that performs your task (and more importantly, keeps your state) into a prototype-scoped bean. Then you can retrieve a fresh instance of that bean from the application context every time your scheduled method is run.
Example
I created a GitHub repository which contains a working example of what I'm talking about, but the gist of it is in these two classes:
ScheduledTask.java
Notice the #Scope annotation. It specifies that this component should not be a singleton. The randomNumber field represents the state that we want to reset with every invocation. "Reset" in this case means that a new random number is generated, just to show that it does change.
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
class ScheduledTask {
private double randomNumber = Math.random();
void execute() {
System.out.printf(
"Executing task from %s. Random number is %f%n",
this,
randomNumber
);
}
}
TaskScheduler.java
By autowiring in ApplicationContext, you can use it inside the scheduledTask method to retrieve a new instance of ScheduledTask.
#Component
public class TaskScheduler {
#Autowired
private ApplicationContext applicationContext;
#Scheduled(cron = "0/5 * * * * *")
public void scheduleTask() {
ScheduledTask task = applicationContext.getBean(ScheduledTask.class);
task.execute();
}
}
Output
When running the code, here's an example of what it looks like:
Executing task from com.thomaskasene.example.schedule.reset.ScheduledTask#329c8d3d. Random number is 0.007027
Executing task from com.thomaskasene.example.schedule.reset.ScheduledTask#3c5b751e. Random number is 0.145520
Executing task from com.thomaskasene.example.schedule.reset.ScheduledTask#3864e64d. Random number is 0.268644
Thomas' approach seems to be a reasonable solution, that's why I upvoted it. What is missing is how this can be applied in the case of a spring batch job. Therefore I adapted his example little bit:
#Component
public class JobCreatorComponent {
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Job createJob() {
// use the jobBuilderFactory to create your job as usual
return jobBuilderFactory.get() ...
}
}
your component with the launch method
#Component
public class ScheduledLauncher {
#Autowired
private ... jobRunner;
#Autwired
private JobCreatorComponent creator;
#Scheduled(cron = "${schedule}")
public void runJob() throws Exception {
// it would probably make sense to check the applicationContext and
// remove any existing job
creator.createJob(); // this should create a complete new instance of
// the Job
jobRunner.runJob(); //runs the batch job by calling jobLauncher.run(job, jobParameters);
}
I haven't tried out the code, but this is the approach I would try.
When constructing the job, it is important to ensure that all reader, processors and writers used in this job are complete new instances as well. This means, if they are not instantiated as pure java objects (not as spring beans) or as spring beans with scope "step" you must ensure that always a new instance is used.
Edited:
How to handle SingeltonBeans
Sometimes singleton beans cannot be prevented, in these cases there must be a way to "reset" them.
An simple approach would be to define an interface "ResetableBean" with a reset method that is implemented by such beans. Autowired can then be used to collect a list of all such beans.
#Component
public class ScheduledLauncher {
#Autowired
private List<ResetableBean> resetables;
...
#Scheduled(cron = "${schedule}")
public void runJob() throws Exception {
// reset all the singletons
resetables.forEach(bean -> bean.reset());
...

What happens when autowired bean is initialized with new constructor?

I have used Spring in the past. I moved to a different team where I am getting familiarized with codebase. I found the following code and trying to understand how it works and how spring injects autowired objects in the case. From my basics of Spring, this is definitely not the right way to do. But surprisingly, this code is in production for a long time and no issues were identified.
#Controller
#RequestMapping("/start")
public class AController implements Runnable, InitializingBean {
#Autowired
private StartServiceImpl service = new StartServiceImpl(); // 1
Thread thread;
public void run() {
service.start();
}
public void stop() {
try {
thread.join();
} catch (InterruptedException e) {
}
}
}
#Override
public void afterPropertiesSet() throws Exception {
thread = new Thread(this);
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
}
#Component
public class StartServiceImpl {
//methods
}
Q1) What does localhost:8080/project/start is expected to do. There is NO GET or POST methods defined.
Q2) on the commented line 1, StartServiceImpl is both autowired and constructed with "new". So what happens here. Does the container inject bean or just an object is instantiated.
#Controller
#RequestMapping("/stop")
public class BController {
#Autowired
private StartServiceImpl service = new StartServiceImpl();
#RequestMapping(value = "**", method = RequestMethod.GET)
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
service.shutdownRequested();
new AController().stop(); // 2
} catch (Exception e) {
}
}
}
Q3) Again in commented line 2, does calling stop, calls the stop on the bean in the application context or a new object gets created and the stop method is called. What would happen in the latter case? Are we really stopping the service that was started or not? I think we are not stopping the service.
I have read this post. It is very useful. But it did not answer my question.
I will try to answer the questions specifically, as the purpose of the code is hard to understand (for me at least).
Q1) It is unclear for me what this code tries to achieve. As you noticed, it is not a controller, and I suspect that the only reason why it is registered this way is so that it gets auto-scanned (which might as well get done by using #Controller. This is just a hunch, I don't quite understand its purpose.
Q2) The answer is that two instances will be created, one via new, the other as a bean. When running in Spring, the final value of the field is the bean, because dependency injection happens after the construction. Typically this is done when the class is envisioned to be used outside Spring (e.g. a unit test), so that the field can be initialized with a default value.
Q3) stop() will be invoked on a new instance, and not the bean. The service bean is stopped because of the direct call above that line to the injected bean, but the next one will be an NPE, I guess, because afterPropertiesSet is not invoked on the target object created via new. the only reason why this doesn't show an NPE in the logs is because the exception is swallowed below. The thread variable is not initialized and remains NULL.
Hope this helps,
This code is flawed on many levels.
Ever since Java 5, manually starting threads is an antipattern. It's messy and way too low-level. ExecutorServices should be used.
A Rest controller that is a Runnable? That's a monstrous mingling of concerns.
A service is created via new but then overwritten with an autowired dependency? WTF!
etc.
I'd keep the thread running all the time, scheduling the task with the #Scheduled annotation and use the controller to toggle a flag that decides if the thread actually does somethin, e.g.
#Service
class StartService{
private boolean active;
public void setActive(boolean active){this.active=active;}
#Scheduled(fixedRate=5000)
public void doStuff(){
if(!active)return;
// do actual stuff here
}
}
And now all the rest controllers do is toggle the value of the "active" field. Benefits:
every class does one thing only
you always know how many threads you have
The code you posted is very strange.
Q1 ) What does localhost:8080/project/start is expected to do. There
is NO GET or POST methods defined.
I think localhost:8080/project/start will return 404 error (The requested resource is not available). Because there is no mapped methods in AController. #RequestMapping annotation on class level is not enough for make request to controller. There is have to be mapped method.
But service will be started anyway. Because AController implements InitializingBean. Method afterPropertiesSet() will be invoked by Spring after controller will be created and all fields will be initialized.
Q2) on the commented line 1, StartServiceImpl is both autowired and
constructed with "new". so what happens here. does the container
inject bean or just an object is instantiated.
Another strange snippet. Java will create new instance of StartServiceImpl on creation of instance of AController class. But after that, Spring will assign it's own instance(declared as component) to this field. And reference to firs instance (created by constructor) will be lost.
Q3) Again in commented line 2, does calling stop, calls the stop on
the bean in the appication context or a new object gets created and
the stop method is called. what would happen in the latter case? Are
we really stopping the service that was started or not? I think we are
not stopping the service
Actually service will be stopped. Because of invocation of service.shutdownRequested();. But thread in AController bean will continue to work. new AController().stop(); will invoke method of just created instance, but not method of controller (instance created by Spring).
This code is totally wrong usage of Spring framework.

Testing #TransactionalEvents and #Rollback

I've been trying to test out #TransactionalEvents (a feature of Spring 4.2 https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2) with our existing Spring JUnit Tests (run via either #TransactionalTestExecutionListener or subclassing AbstractTransactionalUnit4SpringContextTests but, it seems like there's a forced choice -- either run the test without a #Rollback annotation, or the events don't fire. Has anyone come across a good way to test #TransactionalEvents while being able to #Rollback tests?
Stéphane Nicoll is correct: if the TransactionPhase for your #TransactionalEventListener is set to AFTER_COMMIT, then having a transactional test with automatic rollback semantics doesn't make any sense because the event will never get fired.
In other words, there is no way to have an event fired after a transaction is committed if that transaction is never committed.
So if you really want the event to be fired, you have to let the transaction be committed (e.g., by annotating your test method with #Commit). To clean up after the commit, you should be able to use #Sql in isolated mode to execute cleanup scripts after the transaction has committed. For example, something like the following (untested code) might work for you:
#Transactional
#Commit
#Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = TransactionMode.ISOLATED))
#Test
public void test() { /* ... */ }
Regards,
Sam (author of the Spring TestContext Framework)
Marco's solution works but adding REQUIRES_NEW propagation into business code is not always acceptable. This modifies business process behavior.
So we should assume that we can change only test part.
Solutin 1
#TestComponent // can be used with spring boot
public class TestApplicationService {
#Autowired
public MyApplicationService service;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomething() {
service.doSomething();
}
}
This wraps the real service into test component that can be decorated with REQUIRES_NEW propagation. This solution doesn't modify other logic than testing.
Solution 2
#Transactional
#Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = TransactionMode.ISOLATED))
#Test
public void test() {
MyApplicationService target = // ...
target.doSomething();
TestTransaction.flagForCommit(); //Spring-test since 4.1 - thx for Sam Brannen
TestTransaction.end();
// the event is now received by MyListener
// assertions on the side effects of MyListener
// ...
}
This is simplest solution. We can end test transaction and mark it for commit. This forces transactional events to be handled. If test changes data, cleanup sql script must be specified, otherwise we introduce side effects with committed modified data.
Sam Brannen's solution almost works with regard to adam's comment.
Actually the methods annotated with #TransactionalEventListener are called after the test method transaction is committed. This is so because the calling method, which raises the event, is executing within a logical transaction, not a physical one.
Instead, when the calling method is executed within a new physical transaction, then the methods annotated with #TransactionalEventListener are invoked at the right time, i.e., before the test method transaction is committed.
Also, we don't need #Commit on the test methods, since we actually don't care about these transactions. However, we do need the #Sql(...) statement as explained by Sam Brannen to undo the committed changes of the calling method.
See the small example below.
First the listener that is called when the transaction is committed (default behavior of #TransactionalEventListener):
#Component
public class MyListener {
#TransactionalEventListener
public void when(MyEvent event) {
...
}
}
Then the application service that publishes the event listened to by the above class. Notice that the transactions are configured to be new physical ones each time a method is invoked (see the Spring Framework doc for more details):
#Service
#Transactional(propagation = Propagation.REQUIRES_NEW)
public class MyApplicationService {
public void doSomething() {
// ...
// publishes an instance of MyEvent
// ...
}
}
Finally the test method as proposed by Sam Brannen but without the #Commitannotation which is not needed at this point:
#Transactional
#Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = TransactionMode.ISOLATED))
#Test
public void test() {
MyApplicationService target = // ...
target.doSomething();
// the event is now received by MyListener
// assertions on the side effects of MyListener
// ...
}
This way it works like a charm :-)

Spring batch how to use ItemReadListener

I use spring batch for processing a file. The configuration of all components is made programatically.
I have a job that contains several TaskletSteps:
#Bean
#Named(SEEC_JOB)
public Job seecJob() {
return jobBuilderFactory.get(SEEC_JOB).start(seecMoveToWorkingStep()).next(seecLoadFileStep())
.on(ExitStatus.COMPLETED.getExitCode()).to(seecFlowMoveToArchiveOk()).from(seecLoadFileStep())
.on(ExitStatus.FAILED.getExitCode()).to(seecFlowMoveToArchiveKo()).end().build();
}
My question focus on seecLoadFileStep(), the detail bellow:
#Bean
public TaskletStep seecLoadFileStep() {
TaskletStep build = stepBuilderFactory.get(SEEC_LOAD_FILE_STEP)
.<SeecMove, SeecMove>chunk(cormoranProperties.seec.batchSize.get()).reader(seecItemReader())
.writer(seecItemWriter()).build();
return build;
}
I would like to throw a specific exception if a reading error hapens (by reading error I mean: the file is corrupted for example or it is wrong, absent xml tag...).
I have been reading spring batch doc and I think ItemReadListener is my guy:
public interface ItemReadListener<T> extends StepListener {
void beforeRead();
void afterRead(T item);
void onReadError(Exception ex);
}
but, I don't know how to use it! I have tried doing my seecItemReader() implements this interface but onReadError method is never called.
I don't know how to declare/register in the taskletStep the ItemReadListener.
Here a bit of spring doc:
Any class that implements one of the extensions of StepListener (but
not that interface itself since it is empty) can be applied to a step
via the listeners element. The listeners element is valid inside a
step, tasklet or chunk declaration. It is recommended that you declare
the listeners at the level which its function applies, or if it is
multi-featured (e.g. StepExecutionListener and ItemReadListener) then
declare it at the most granular level that it applies (chunk in the
example given).
An ItemReader, ItemWriter or ItemProcessor that itself implements one
of the StepListener interfaces will be registered automatically with
the Step if using the namespace element, or one of the the
*StepFactoryBean factories. This only applies to components directly injected into the Step: if the listener is nested inside another
component, it needs to be explicitly registered (as described above).
Could you please help me?
Thanks in advance!
As I guessed it was easier than I thougth, for registering programatically the ItemReadListener is via listener method in the tasklet configuration:
#Bean
public TaskletStep seecLoadFileStep() {
TaskletStep build = stepBuilderFactory.get(SEEC_LOAD_FILE_STEP)
.<SeecMove, SeecMove>chunk(cormoranProperties.seec.batchSize.get()).reader(seecItemReader()).listener(seecItemReaderListener())
.writer(seecItemWriter()).build();
return build;
}
And now the onError method is called when an Exception happens.

Resources