On stopping a job the manual says
The shutdown is not immediate, since there is no way to force immediate shutdown, especially if the execution is currently in developer code that the framework has no control over, such as a business service.
My long-running steps performs checks for Thread.currentThread().isInterrupted() and throws InterruptedException. Surely there is some way to interrupt the thread running the job?
(Note: I'm using SimpleAsyncTaskExecutor with TaskletStep)
Create a bean to implement
ApplicationListener<ContextClosedEvent> onApplicationEvent()
Then you can shut down the task.
#Component
class ContextClosedHandler implements ApplicationListener<ContextClosedEvent> {
#Autowired SimpleAsyncTaskExecutor executor;
void onApplicationEvent(ContextClosedEvent event) {
executor.shutdown();
}
}
This post about the lifecycle of beans might help as well
http://technicalmumbojumbo.wordpress.com/2011/04/13/spring-framework-an-introduction-part-ii-object-lifecycle-autowiring-internationalizationi18n/
Related
I am scheduling a task that runs at fixed rate in Spring boot. The function that I am using to schedule a a task is as below:
private void scheduleTask(Store store, int frequency) {
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = store::scan;
scheduler.scheduleAtFixedRate(task, 0, frequency, TimeUnit.MILLISECONDS);
}
This works fine but if if there is an exception at application startup, the application should exit on exception. What is happening is that I get the exception in the log and the message "Application Failed to start" but looks like the scheduler shows as still running although it looks like only the scheduled thread is still running.
Any hints on how to properly schedule an asynchronous task in a Spring boot application? I tried the #Scheduled annotation but it does not run at all.
The #Scheduled should work. Have you added the #EnabledScheduling annotation to a #Configuration or the #SpringBootApplication? The Scheduling Getting Started explains it in detail.
Regarding the scheduleTask method: What calls that? Is it started outside the Spring context? If yes then Spring won't stop it. You have to take care of the lifecycle.
You should try to use the #Scheduled as it will manage the thread pools/executors for you and most people will find it easier to understand.
What is happening to me is that the MDB receives messages and tries to process them and even my server has not started completely
any idea how to solve this?
You can find out if your server startup is completed by one of the following two techniques:
use ServletContextListener, once your application deployment is complete, server would call ServletContextListener.contextInitialized method
Use mbean support from wildfly, you can query mBean via JMX interface of wildfly and figure out if the server state is 'started'. But mind you, your code would be tied down to wildfly only in this case.
Once you decide the option to figure out the server startup state, you need to check for it in your MDB's postconstruct method and go ahead only if the server is started.
#MessageDriven(...)
public class MyMdb implements MessageListener {
#PostConstruct
public void init() {
// check if server has started here
//if server is not started, sleep and re-check again.
}
public void onMessage(Message message) {
}
}
I am writing Spring (Java 8) web application and per each request(separate thread) my application make a few tasks, which should be completed as quickly as possible to return result to client in browser, so I'd like to find worker pool library, which can be accessed from different threads in safe way.
I have read about Execution pool, Rabbit MQ, but I couldn't find information about the feature of accessing the tasks queue from different threads.
I will really appreciate if somebody can give me advice how to do it in Java.
Here you can use asynchronous method call supported by Spring via #Async annotation.
The #Async annotated method will be executed in new thread and the result will be available in Future object (void return is also supported). Do note that the method call is non-blocking which will help return the response quickly without waiting for every task to complete. However if required the main thread can be made to wait for all task i.e. Future to complete via Future.get() which is blocking.
To configure the asynchronous support, annotate the configuration class with #EnableAsync and provide below method definition as described here.
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.initialize();
return executor;
}
In my application using Spring Batch 3.0.1 I need to get access to the thread created by SimpleAsyncTaskExecutor.createThread(Runnable runnable) before it is started in doExecute.
This needs to be done to attach security context stuff to the newly created thread.
Of course I could extend SimpleAsyncTaskExecutor and use this class, but that seems a crude solution.
I tried using Spring AOP and interceptors, but cannot intercept the right method.
Any ideas?
Edit:
protected void doExecute(Runnable task) {
Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
thread.start();
}
I believe the correct way to handle this type of behavior would be to create your own ThreadFactory and pass that to the SimpleAsyncTaskExecutor. That would allow you access to the threads as they are being created.
You can read more about the SimpleAsyncTaskExecutor here: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/task/SimpleAsyncTaskExecutor.html
You can read more about the ThreadFactory here: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadFactory.html
I have a situation where I have to invoke the batch process and update the status of the job in my service layer. Here the JobRepositoryFactoryBean already consists of transaction manager so I should not annotate my service method with #Transactional, If I annotated I will get exception at runtime saying that "Existing transaction detected in job repository please fix and try again by removing #Transactional" If not annotated with #Transactional I am getting runtime exception saying that "no session found for current thread". Please help me in this to resolve it.
Services should not interact directly with JobRepositoryFactoryBean or the Job Repository itself. It’s not recommend to use the JobRepository API outside the Spring Batch infrastructure.
The Database tables let you follow the execution of batch jobs and see what happens at both the job and step levels for more information see here. In case you think you need an additional data persisted about the job add your own table.
To update the status of the Job itself use the
jobExecution.setExitStatus or jobExecution.setStatus API.
Read about the difference bewteeb ExitStatus and BatchStatus here.
I suggest using a a job listener in your case.
Job Listener are unlike tasklet or step listener are executed outside of the chunk’s transaction. See the following blog post here
#Component
public class JobMonitorListener {
private static Log LOGGER = LogFactory.getLog(JobMonitorListener.class);
#Autowired
JobMonitorService monitorService;
#BeforeJob
public void beforeJob(JobExecution jobExecution){
LOGGER.info("Before Job");
monitorService.persistAddtionalData(date);
}
#AfterJob
public void afterJob(JobExecution jobExecution){
LOGGER.info("Afetr Job");
monitorService.persistAddtionalData(date);
jobExecution.setExitStatus(new ExitStatus("Failed by Monitor"));
jobExecution.setStatus(BatchStatus.FAILED);
}
}
The service:
#Component
public class JobMonitorServiceImpl implements JobMonitorService {
#Transactional("transactionManager")
public void persistAddtionalData(Object) {
}
}
your job xml
<batch:job id="job">
<batch:listeners >
<batch:listener ref="jobMonitorListener"/>
</batch:listeners>
...
</batch:job>
Make sure you are using a transaction manager different then the one used by the JobRepostory. See my answer here for more Transaction Issue with Spring Batch JobRepository in Unit Test
Anyway using #Transactional with Spring batch is tricky (see the comment Spring Batch. Call methods with rollbackFor by Michael Minella the project lead of Spring Batch).