I run a spring web application (not spring boot).
On startup the application requests a dataset from another server. This dataset takes about a minute to be processed. When I deploy this application to the tomcat, it takes a minute. The website itself will not be available until the dataset request has been processed completely. But actually I would like to see, that users are already able to login and the dataset is processed without stopping the rest of the application from working.
Currently I have a service class and use the #PostConstruct-Annotation.
#Service
public class StartupService {
#PostConstruct
public void load() {
//perform the dataset request
...
}
}
I found similar article here on stackoverflow, that suggested trying the ApplicationListener. But this has the same effect. HTTP Requests to the website are not answered unless the dataset request has finished.
#Service
public class StartupService implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
//perform the dataset request
...
}
}
Of course it would be possible, to start a new Thread, but I would like to know, what the best approach for this problem would be.
From the PostConstruct doc:
... This method MUST be invoked before the class is put into service...
So Spring cannot service a request until the #PostContruct method finishes.
As you suggested, start a new thread manually, or:
from the #PostConstruct method, call a public method in another bean annotated with #Async and Spring will asynchronously invoke the method which will allow the #PostConstruct method to finish immediately and start servicing requests
from the #PostConstruct method, #Schedule a one time task - for example 1 minute from now
See also: #EnableAsync and/or #EnableScheduling
Related
Using Spring Boot 2.1.1.RELEASE / Spring Framework 5.1.4, I have an application with #Async and #Transactional annotations enabled through:
#EnableAsync(mode = AdviceMode.ASPECTJ)
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
When running a method that is annotated with both, first the transaction is created and then the asynchronous execution starts. So, the actual method body is not executed inside the transaction.
#Transactional
#Async
public void myAsyncMethod() {
// asynchronous database stuff
}
How can I configure spring / the aspects to actually execute in an order that makes sense, e.g. start the transaction on the new thread?
On a side note, with the older Spring Boot 1.5.17 / Spring Framework 4.3.20 it actually worked.
Demo: https://github.com/jaarts/spring-asynctransaction-demo
In Spring 5 Async advice is always execited first. See AsyncAnnotationBeanPostProcessor
public AsyncAnnotationBeanPostProcessor() {
setBeforeExistingAdvisors(true);
}
After that on superclass in postProcessAfterInitialization when advisors aplies code executes
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
}
On #EnableTransactionManagement#order javadoc says
Indicate the ordering of the execution of the transaction advisor
but on #EnableAsync
Indicate the order in which the AsyncAnnotationBeanPostProcessor should be applied.
As per the class level comment
org.springframework.cloud.context.environment.EnvironmentChangeEvent
Event published to signal a change in the {#link Environment}.
This is too generic and I do not know what are all included when we say change in the Environment.
org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent
Sadly this class does not have a class comment.
My understanding is that RefreshScopeRefreshedEvent handles changes in property values in config-server but looks like it does not instead such event is available through EnvironmentChangeEvent.
I did hit /refresh endpoint but it does not call my listener class with RefreshScopeRefreshedEvent event. Instead EnvironmentChangeEvent got a call for /refresh
EnvironmentChangeEvent is fired when there's a change in Environment. In terms of Spring Cloud Config it means it's triggered when /env actuator endpoint is called.
RefreshScopeRefreshedEvent is fired when refresh of #RefreshScope beans has been initiated, e.g. /refresh actuator endpoint is called.
You can by registering ApplicationListener like that:
#Configuration
public class ApplicationConfig {
#EventListener(RefreshScopeRefreshedEvent.class)
public void onRefresh(RefreshScopeRefreshedEvent event) {
// Your code goes here...
}
}
I have set transaction timeout in my application as #Transactional(propagation=Propagation.REQUIRED,timeout=30)
ActiveMQXAConnectionFactory and Oracle XA Datasource are two resources of my Distributed transaction. after reading a message from queue my transaction begins and while processing the application is taking more than 30 seconds and still transaction is not timed out. Only when committing the transaction its throwing timeout exception. I wanted immediately after 30 seconds the transaction should time out and throw the exception and make that thread available to consume another message from queue. Is this possible?
Without seeing your configuration it will be hard to say. If you are just adding an #Transactional, it is not going to do anything. You going to need both an EntityManager and a TransactionManager, then you need to turn on annotation based transaction management, and Spring needs to be controlling your datasource if I recall correctly.
Another, probably unnecessary side note, #Transactional will only work on public methods. Spring will proxy your method in order to manage the transaction and Spring can only proxy public methods. Also, it will only be able to work on calls from another class to that method, if you are calling that method from another method inside the same class, Spring cannot proxy either, thus no transaction management. Spring is sneakily deceptive here.
#Service
public class A{
#Autowired
Datasource datasource;
#Transactional
public void save(){
datasource.doStuff();
}
public void callSave(){
save();
}
}
#Service
public class B{
#Autowired
A a;
public void callSave(){
a.save();
}
}
Here, if a.save() is called from a.callSave(), no proxy will occur, thus you will have no transaction management. But in the exact same application, if you call b.callSave(), you will have transaction management, since Spring can then proxy the method call to a.save().
Are you using Spring Boot or vanilla Spring? We can probably give you more of a direction if you divulge that.
Hopefully that helped a bit!
I have an application listener that's supposed to execute only once per webapp startup, since it loads basic user info data.
public class DefaultUsersDataLoader implements ApplicationListener<ContextRefreshedEvent> {
#Override
#Transactional
public void onApplicationEvent(ContextRefreshedEvent e) {...}
}
Somehow, it gets executed twice: on app startup and when the first request arrives to the server. Why is this happening and how can I prevent it?
Generally in a Spring MVC application you have both a ContextLoaderListener and DispatcherServlet. Both components create their own ApplicationContext which in turn both fire a ContextRefreshedEvent.
The DispatcherServlet uses the ApplicationContext as created by the ContextLoaderListener as its parent. Events fired from child contexts are propagated to the parent context.
Now if you have an ApplicationListener<ContextRefreshedEvent> defined in the root context (the one loaded by the ContextLoaderListener) it will receive an event twice.
Do not annotated your Listener Class's method with #EventListener
We're using Spring 3.1, JPA (via Hibernate) and Quartz. Typically we interact with the DB via #PersistenceContext annotation on Service beans, and either SpringMVC controllers, or GraniteDS-managed service invocation.
I'm working on writing a Quartz job that needs to interact with the database. I've tried everything I can find to get this working. I tried passing in a Spring-managed component (annotated with #PersistenceContext and #Transactional) via the jobMap, the call to entityManager.persist(o) executes, but nothing happens in the database. I also tried similar to this answer, creating a factory class to call autowireBean() on the job object. I set up the job class like so:
public class CreateAlertJob implements Job {
#PersistenceContext
EntityManager entityManager;
#Override
#Transactional
public void execute(JobExecutionContext context) throws JobExecutionException {
SomeEntity entity = new SomeEntity();
entityManager.persist(entity);
}
}
Same result, the method executes but the database is unaltered. I found this blog post which references a GitHub project. There he is using JpaInterceptor to establish a Hibernate session, but this uses the DAO pattern and I'd like to stick with using #PersistenceContext.
Clearly there is something about the Quartz thread that is preventing this from working properly? I'm about out of ideas and considering making a web service call to a SpringMVC controller just to get this working.
Since your CreateAlertJob is not created by Spring, #Transactional in it doesn't take effect.
You have the following options:
Delegate actual work to Spring bean and put #Transactional there
Use programmatic transaction management
Use AspectJ-based AOP implementation instead of Spring default implementation (but it would be overkill for such a simple problem)