How to close Spring beans properly after received a SIGTERM? - java

I'm trying to keep my application alive in order to listen for some messages from my queue. However, once my application receives a SIGTERM, I would like to ensure that my application shutdown nicely. Meaning, ensure that the jobs, internally, has finished first, before the shutdown.
After reading about it , I came up with this:
#Component
public class ParserListenerStarter {
public static void main(final String[] args) throws InterruptedException {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ParserReceiveJmsContext.class);
context.registerShutdownHook();
System.out.println("Listening...");
Runtime.getRuntime().addShutdownHook( //
new Thread() {
#Override
public void run() {
System.out.println("Closing parser listener gracefully!");
context.close();
}
});
while (true) {
Thread.sleep(1000);
}
}
}
Then I send a kill command to my application; and this is my output:
Listening...
// output from my other beans here
Closing parser listener gracefully!
Process finished with exit code 143 (interrupted by signal 15: SIGTERM)
The shutdown methods from my beans were not called:
#PreDestroy public void shutdown() {..}
I'm not an expert in Spring, so I'm sorry for any silly point that I'm missing here.
How can I shutdown my beans and then close my application nicely ?

All you need:
context.registerShutdownHook();
So, add the code above and then your #PreDestroy method will be invoked.
After that you don't need to do anything else. It means you must can delete
Runtime.getRuntime().addShutdownHook( //
new Thread() {
#Override
public void run() {
System.out.println("Closing parser listener gracefully!");
context.close();
}
});
EDIT:
Documentation says that you can have multiple shutdown hooks:
When the virtual machine begins its shutdown sequence it will start all registered shutdown hooks
So the statement below is incorrect:
When you added this, you replaced the Spring hook, which do the beans destroying, because internally, method looks like
if (this.shutdownHook == null) {
// No shutdown hook registered yet.
this.shutdownHook = new Thread() {
#Override
public void run() {
doClose();
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}

Related

Spring boot JPA repository passed to another thread not working

I have an autowired jpa repository object working. However, I need to use it to add rows into the database from multiple threads.
Though, after passing it to another thread, it fails.
Code structure
#SpringBootApplication(exclude = HealthcheckConfig.class)
public class Application implements CommandLineRunner {
#Autowired
private DBRepository dbRepository;
#Autowired
private AppConfig appConfig;
private ExecutorService executors = Executors.newFixedThreadPool(3);
Application() {
}
#Override
public void run(final String... args) {
final DBSchemaObject temp = new Application("testdb", "testfield");
dbRepository.save(temp); // This WORKs!!!
for (FileStatus fileStatus: fileStatuses) {
executors.execute(new ThreadSafeClass(dbRepository));
}
}
public static void main(final String[] args) {
new SpringApplicationBuilder(Application.class)
.web(WebApplicationType.NONE)
.run(args)
.close();
}
}
However, doing a dbRepository.save() from a thread safe class, I get error
cause: java.lang.IllegalStateException: org.springframework.context.annotation.AnnotationConfigApplicationContext#41330d4f has been closed already
detailedMessage: Error creating bean with name 'spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties': Could not bind properties to 'DataSourceProperties' : prefix=spring.datasource, ignoreInvalidFields=false, ignoreUnknownFields=true
Stacktrace:
{StackTraceElement#14839} "org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)"
{StackTraceElement#14840} "org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)"
{StackTraceElement#14841} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354)"
{StackTraceElement#14842} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)"
{StackTraceElement#14843} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)"
{StackTraceElement#14844} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)"
{StackTraceElement#14845} "org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)"
{StackTraceElement#14846} "org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)"
{StackTraceElement#14847} "org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)"
{StackTraceElement#14848} "org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)"
{StackTraceElement#14849} "org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:410)"
{StackTraceElement#14850} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1334)"
{StackTraceElement#14851} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)"
{StackTraceElement#14852} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)"
{StackTraceElement#14853} "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)"
{StackTraceElement#14854} "org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)"
{StackTraceElement#14855} "org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)"
{StackTraceElement#14856} "org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)"
{StackTraceElement#14857} "org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)"
{StackTraceElement#14858} "org.springframework.beans.factory.support.DefaultListableBeanFactory$1.orderedStream(DefaultListableBeanFactory.java:481)"
{StackTraceElement#14859} "org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.detectPersistenceExceptionTranslators(PersistenceExceptionTranslationInterceptor.java:167)"
{StackTraceElement#14860} "org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:149)"
{StackTraceElement#14861} "org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)"
{StackTraceElement#14862} "org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174)"
{StackTraceElement#14863} "org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)"
{StackTraceElement#14864} "org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)"
{StackTraceElement#14865} "org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)"
{StackTraceElement#14866} "org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)"
{StackTraceElement#14867} "com.sun.proxy.$Proxy99.save(Unknown Source)"
{StackTraceElement#14868} "com.xxxx.run(Application.java:109)"
{StackTraceElement#14869} "java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)"
{StackTraceElement#14870} "java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)"
{StackTraceElement#14871} "java.lang.Thread.run(Thread.java:748)"
How can I use the spring boot repository object across multiple threads ?
The problem is that your run() method just schedules the tasks to be executed, but does not wait for their completion. This what is happening:
new SpringApplicationBuilder(Application.class) You are creating a new application context with the command line runner Application
.run(args) Then you initialize and execute your application context's run() method
The run() method schedules the tasks to be executed and exists immediately:
public void run(final String... args) {
for (FileStatus fileStatus: fileStatuses) {
executors.execute(new ThreadSafeClass(dbRepository));
}
}
Because run() terminated, spring assumes that the application has finished and calls .close(); Thus closing the application context and making it impossible to use any spring features such as repositories.
The scheduled tasks get executed, but the context was already closed, thus they fail and throw the exception.
The solution is to wait for the tasks' completion before exiting from the run method. As your example is too minimal, this is just an example. Alternatively you can use other methods to wait for the completion of the tasks such as CountDownLatch , etc, without having to shutdown the thread pool:
for (FileStatus fileStatus: fileStatuses) {
executors.execute(new ThreadSafeClass(dbRepository));
}
executors.shutdown(); // prevents the executor from accepting any new tasks
executors.awaitTermination(); // wait for the tasks to finish
ExecutorService::shutdown javadoc
ExecutorService::awaitTermination javadoc

Stop all spring batch jobs at shutdown (CTRL-C)

I have a spring boot / spring batch application, which starts different jobs.
When the app is stopped (CTRL-C) the jobs are left in the running state (STARTED).
Even though CTRL-C gives the app enough time to gracefully stop the jobs the result is the same as a kill -9.
I've found a way (see below) to gracefully stop all jobs when the application is killed using CTRL-C, but would like to know if there is a better / simpler way to achieve this goal.
Everything below is documentation on how I managed to stop the jobs.
In a blog entry from 부알프레도 a JobExecutionListener is used to register shutdown hooks which should stop jobs:
public class ProcessShutdownListener implements JobExecutionListener {
private final JobOperator jobOperator;
ProcessShutdownListener(JobOperator jobOperator) { this.jobOperator = jobOperator; }
#Override public void afterJob(JobExecution jobExecution) { /* do nothing. */ }
#Override
public void beforeJob(final JobExecution jobExecution) {
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
super.run();
try {
jobOperator.stop(jobExecution.getId());
while(jobExecution.isRunning()) {
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
} catch (NoSuchJobExecutionException | JobExecutionNotRunningException e) { /* ignore */ }
}
});
}
}
In addition to the provided code I also had to create a JobRegistryBeanPostProcessor.
Without this PostProcessor the jobOperator would not be able to find the job.
(NoSuchJobException: No job configuration with the name [job1] was registered
#Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
postProcessor.setJobRegistry(jobRegistry);
return postProcessor;
}
The shutdown hook was not able to write the state to the database, as the database connection was already closed:
org.h2.jdbc.JdbcSQLNonTransientConnectionException: Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL)
Processing item 2 before
Shutdown Hook is running !
2021-02-08 22:39:48.950 INFO 12676 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2021-02-08 22:39:49.218 INFO 12676 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Processing item 3 before
Exception in thread "Thread-3" org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30004ms.
In order to make sure that spring boot doesn't close the hikari datasource pool before having stopped the jobs I used a SmartLifeCycle as mentioned here.
The final ProcessShutdownListener looks like:
#Component
public class ProcessShutdownListener implements JobExecutionListener, SmartLifecycle {
private final JobOperator jobOperator;
public ProcessShutdownListener(JobOperator jobOperator) { this.jobOperator = jobOperator; }
#Override
public void afterJob(JobExecution jobExecution) { /* do nothing. */ }
private static final List<Runnable> runnables = new ArrayList<>();
#Override
public void beforeJob(final JobExecution jobExecution) {
runnables.add(() -> {
try {
if (!jobOperator.stop(jobExecution.getId())) return;
while (jobExecution.isRunning()) {
try {
Thread.sleep(100);
} catch (InterruptedException ignored) { /* ignore */ }
}
} catch (NoSuchJobExecutionException | JobExecutionNotRunningException e) { /* ignore */ }
});
}
#Override
public void start() {}
#Override
public void stop() {
// runnables.stream()
// .parallel()
// .forEach(Runnable::run);
runnables.forEach(Runnable::run);
}
#Override
public boolean isRunning() { return true; }
#Override
public boolean isAutoStartup() { return true; }
#Override
public void stop(Runnable callback) { stop(); callback.run(); }
#Override
public int getPhase() { return Integer.MAX_VALUE; }
}
This listener has to be registered when configuring a job:
#Bean
public Job job(JobBuilderFactory jobs,
ProcessShutdownListener processShutdownListener) {
return jobs.get("job1")
.listener(processShutdownListener)
.start(step(null))
.build();
}
Finally as mentioned in the exception output the flag: ;DB_CLOSE_ON_EXIT=FALSE must be added to the jdbc url.
This approach is the way to go, because shutdown hooks are the only way (to my knowledge) offered by the JVM to intercept external signals. However, this approach is not guaranteed to work because shutdown hooks are not guaranteed to be called by the JVM. Here is an excerpt from the Javadoc of Runtime.addShutdownHook method:
In rare circumstances the virtual machine may abort, that is, stop running
without shutting down cleanly. This occurs when the virtual machine is
terminated externally, for example with the SIGKILL signal on Unix or
the TerminateProcess call on Microsoft Windows.
Moreover, shutdown hooks are expected to run "quickly":
Shutdown hooks should also finish their work quickly. When a program invokes
exit the expectation is that the virtual machine will promptly shut down
and exit.
In your case, JobOperator.stop involves a database transaction (which might cross a network) to update the job's status, and I'm not sure if this operation is "quick" enough.
As a side note, there is an example in the samples module called GracefulShutdownFunctionalTests. This example is based on JobExecution.stop which is deprecated, but it will be updated to use JobOperator.stop.

How to keep apache camel context alive in thread main

I'm trying to make simple application that will listen one queue from artemis and then proceed messages and after that create new message in second queue.
I have created in method Main Camel context and added routing (it forwards messages to bean). And to test this routing and that this bean works correctly I'm sending
few messages to this queue - rigth after context started in main thread
public static void main(String args[]) throws Exception {
CamelContext context = new DefaultCamelContext();
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616", "admin", "admin");
context.addComponent("cmp/q2", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));
context.addRoutes(new RouteBuilder() {
public void configure() {
from("cmp/q2:cmp/q2").bean(DataRequestor.class, "doSmth(${body}, ${headers})");
}
});
ProducerTemplate template = context.createProducerTemplate();
context.start();
for (int i = 0; i < 2; i++) {
HashMap<String, Object> headers = new HashMap<String, Object>();
headers.put("header1", "some header info");
template.sendBodyAndHeaders("cmp/q2:cmp/q2", "Test Message: " + i, headers);
}
context.stop();
}
And in this case application works fine, but it stops when method main completed - it proceess only messages that were created by it self.
Now after I have test bean that is used in routing, I want to modify application such way that it should start and stay active(keeping camle context and routin alive ) - so that i can create massages manually in web UI (active mq management console).
But I really don't know how.
I have tried infinite loop with Thread.sleep(5000);
I tried to start one more thread(also with infinite loop) in main method.
But it didn't work.(The most suspicious for me in case with infinite loop is that apllication is running, but when i create message in web UI it just desapears - and no any traces in system out that it was processed by my bean in routing, a suppose that it should be processed by my bean or just stay in the queue untouched, but it just disapears).
I now that my question is dummy, but I already have wasted 3 days to find a solution, so any advices or link to tutorials or some valueable information are appreciated.
PS: I've got one painfull restriction - Spring frameworks are not allowed.
I think the most simple solution for running standalone camel is starting it with camel Main. Camel online documentation has also an example of using it http://camel.apache.org/running-camel-standalone-and-have-it-keep-running.html.
I will copy paste the example code here just in case:
public class MainExample {
private Main main;
public static void main(String[] args) throws Exception {
MainExample example = new MainExample();
example.boot();
}
public void boot() throws Exception {
// create a Main instance
main = new Main();
// bind MyBean into the registry
main.bind("foo", new MyBean());
// add routes
main.addRouteBuilder(new MyRouteBuilder());
// add event listener
main.addMainListener(new Events());
// set the properties from a file
main.setPropertyPlaceholderLocations("example.properties");
// run until you terminate the JVM
System.out.println("Starting Camel. Use ctrl + c to terminate the JVM.\n");
main.run();
}
private static class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("timer:foo?delay={{millisecs}}")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
System.out.println("Invoked timer at " + new Date());
}
})
.bean("foo");
}
}
public static class MyBean {
public void callMe() {
System.out.println("MyBean.callMe method has been called");
}
}
public static class Events extends MainListenerSupport {
#Override
public void afterStart(MainSupport main) {
System.out.println("MainExample with Camel is now started!");
}
#Override
public void beforeStop(MainSupport main) {
System.out.println("MainExample with Camel is now being stopped!");
}
}
}
The route keeps executing until you hit Ctlr+c or stop it in some other way...
If you test this, notice that you need example.properties file in your classpath, with the property millisecs.
At the very minimum you need a main thread to kick off a thread to run the camel route and then check for when that thread is done. The simple java threading approach using the main loop to check .wait() and the end of the camel route thread to signal .notify() when it finishes (or shutdown) would get the job done.
From there you can look into an executor service or use a micro-container like Apache Karaf
PS. Props for going Spring-free!
Disclaimer: this is written in Kotlin but it is somewhat trivial to port to java
Disclaimer: this is written for Apache-Camel 2.24.2
Disclaimer: I am also learning about Apache-Camel. The docs are a little heavy for me
I tried the Main route to set it up but it quickly got a little convoluted. I know that this is a java thread but I'm using kotlin ATM, I'll leave most of the types and imports available so it's easier for java devs.
class Listener
The first I had to fight with was understanding the lifecycle of Main. It turns out that there is an interface you can implement to add in the implementations of such events. With such an implementation I can hook up any routines that have to be sure that camel has started (no guessing required).
import org.apache.camel.CamelContext
import org.apache.camel.main.MainListener
import org.apache.camel.main.MainSupport
typealias Action = () -> Unit
class Listener : MainListener {
private var afterStart: Action? = null
fun registerOnStart(action:Action) {
afterStart = action
}
override fun configure(context: CamelContext) {}
override fun afterStop(main: MainSupport?) {}
override fun afterStart(main: MainSupport?) {
println("started!")
afterStarted?.invoke().also { println("Launched the registered function") }
?: println("Nothing registered to start")
}
override fun beforeStop(main: MainSupport?) {}
override fun beforeStart(main: MainSupport?) {}
}
class ApplicationCore
Then I set up the configuration of the context (Routes, Components, etc,...)
import org.apache.camel.CamelContext
import org.apache.camel.impl.DefaultCamelContext
import org.apache.camel.impl.SimpleRegistry
import org.apache.camel.main.Main
class ApplicationCore : Runnable {
private val main = Main()
private val registry = SimpleRegistry()
private val context = DefaultCamelContext(registry)
private val listener = Listener() // defined above
// for Java devs: this is more or less a constructor block
init {
main.camelContexts.clear()
listener.registerOnStart({ whateverYouAreDoing().start() })// <- your stuff should run in its own thread because main will be blocked
main.camelContexts.add(context)
main.duration = -1
context.addComponent("artemis", ...)// <- you need to implement your own
context.addRoutes(...)// <- you already know how to do this
...// <- anything else you could need to initialize
main.addMainListener(listener)
}
fun run() {
/* ... add whatever else you need ... */
// The next line blocks the thread until you close it
main.run()
}
fun whateverYouAreDoing(): Thread {
return Thread() {
ProducerTemplate template = context.createProducerTemplate();
for (i in 0..1) {
val headers = HashMap<String, Any>()
headers["header1"] = "some header info"
template.sendBodyAndHeaders("cmp/q2:cmp/q2", "Test Message: $i", headers)
}
context.stop()// <- this is not good practice here but its what you seem to want
}
}
}
In kotlin, initialization is rather easy. You can easily translate this into java because it is quite straight forward
// top level declaration
fun main(vararg args:List<String>) = { ApplicationCore().run() }

EJB #Schedule wait until method completed

I want to write a back-ground job (EJB 3.1), which executes every minute. For this I use the following annotation:
#Schedule(minute = "*/1", hour = "*")
which is working fine.
However, sometimes the job may take more than one minute. In this case, the timer is still fired, causing threading-issues.
Is it somehow possible, to terminate the scheduler if the current execution is not completed?
If only 1 timer may ever be active at the same time, there are a couple of solutions.
First of all the #Timer should probably be present on an #Singleton. In a Singleton methods are by default write-locked, so the container will automatically be locked-out when trying to invoke the timer method while there's still activity in it.
The following is basically enough:
#Singleton
public class TimerBean {
#Schedule(second= "*/5", minute = "*", hour = "*", persistent = false)
public void atSchedule() throws InterruptedException {
System.out.println("Called");
Thread.sleep(10000);
}
}
atSchedule is write-locked by default and there can only ever be one thread active in it, including calls initiated by the container.
Upon being locked-out, the container may retry the timer though, so to prevent this you'd use a read lock instead and delegate to a second bean (the second bean is needed because EJB 3.1 does not allow upgrading a read lock to a write lock).
The timer bean:
#Singleton
public class TimerBean {
#EJB
private WorkerBean workerBean;
#Lock(READ)
#Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
public void atSchedule() {
try {
workerBean.doTimerWork();
} catch (Exception e) {
System.out.println("Timer still busy");
}
}
}
The worker bean:
#Singleton
public class WorkerBean {
#AccessTimeout(0)
public void doTimerWork() throws InterruptedException {
System.out.println("Timer work started");
Thread.sleep(12000);
System.out.println("Timer work done");
}
}
This will likely still print a noisy exception in the log, so a more verbose but more silently solution is to use an explicit boolean:
The timer bean:
#Singleton
public class TimerBean {
#EJB
private WorkerBean workerBean;
#Lock(READ)
#Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
public void atSchedule() {
workerBean.doTimerWork();
}
}
The worker bean:
#Singleton
public class WorkerBean {
private AtomicBoolean busy = new AtomicBoolean(false);
#Lock(READ)
public void doTimerWork() throws InterruptedException {
if (!busy.compareAndSet(false, true)) {
return;
}
try {
System.out.println("Timer work started");
Thread.sleep(12000);
System.out.println("Timer work done");
} finally {
busy.set(false);
}
}
}
There are some more variations possible, e.g. you could delegate the busy check to an interceptor, or inject a singleton that only contains the boolean into the timer bean, and check that boolean there, etc.
I ran into the same problem but solved it slightly differently.
#Singleton
public class DoStuffTask {
#Resource
private TimerService timerSvc;
#Timeout
public void doStuff(Timer t) {
try {
doActualStuff(t);
} catch (Exception e) {
LOG.warn("Error running task", e);
}
scheduleStuff();
}
private void doActualStuff(Timer t) {
LOG.info("Doing Stuff " + t.getInfo());
}
#PostConstruct
public void initialise() {
scheduleStuff();
}
private void scheduleStuff() {
timerSvc.createSingleActionTimer(1000l, new TimerConfig());
}
public void stop() {
for(Timer timer : timerSvc.getTimers()) {
timer.cancel();
}
}
}
This works by setting up a task to execute in the future (in this case, in one second). At the end of the task, it schedules the task again.
EDIT: Updated to refactor the "stuff" into another method so that we can guard for exceptions so that the rescheduling of the timer always happens
Since Java EE 7 it is possible to use an "EE-aware" ManagedScheduledExecutorService, i.e. in WildFly:
In for example a #Singleton #Startup #LocalBean, inject the default "managed-scheduled-executor-service" configured in standalone.xml:
#Resource
private ManagedScheduledExecutorService scheduledExecutorService;
Schedule some task in #PostConstruct to be executed i.e. every second with fixed delay:
scheduledExecutorService.scheduleWithFixedDelay(this::someMethod, 1, 1, TimeUnit.SECONDS);
scheduleWithFixedDelay:
Creates and executes a periodic action that becomes enabled first
after the given initial delay, and subsequently with the given delay
between the termination of one execution and the commencement of the
next.[...]
Do not shutdown the scheduler in i.e. #PreDestroy:
Managed Scheduled Executor Service instances are managed by the
application server, thus Java EE applications are forbidden to invoke
any lifecycle related method.
well I had a similar problem. There was a job that was supposed to run every 30 minutes and sometimes the job was taking more than 30 minutes to complete in this case another instance of job was starting while previous one was not yet finished.
I solved it by having a static boolean variable which my job would set to true whenever it started run and then set it back to false whenever it finished. Since its a static variable all instances will see the same copy at all times. You could even synchronize the block when u set and unset the static variable.
class myjob{
private static boolean isRunning=false;
public executeJob(){
if (isRunning)
return;
isRunning=true;
//execute job
isRunning=false;
}
}

What should the Java main method be for a standalone application (for Spring JMS)?

I am interested in creating a Spring standalone application that will run and wait to receive messages from an ActiveMQ queue using Spring JMS. I have searched a lot of places and cannot find a consistent way of implementing the main method for such a standalone application. There appears to be few examples of Spring standalone applications. I have looked at Tomcat, JBoss, ActiveMQ and other examples from the around the web but I have not come to a conclusion so ...
What is the best practice for implementing a main method for a Java application (specifically Spring with JMS) ?
Update:
Here's an example from: http://forum.springsource.org/showthread.php?t=48197
Is this the best way of doing this?
public static void main(String args[]) {
try {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
. . . . .
Object lock = new Object();
synchronized (lock) {
lock.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
}
When using Spring JMS you already use components/beans in your configuration that get auto-started and will stay alive (subscribe and read from queue/topic) until you stop the application.
To start and keep the application running, loading the applicationcontext should therefore be enough.
Good practice though is to also call the registerShutdownHook, so on a application halt (i.e via ctrl+c in the console), al your beans are gracefully shutdown and disposed :)
public static void main(String args[]) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
context.registerShutdownHook();
}
This is what we have, inside app-context.xml we use spring JMS classes like (org.springframework.jms.listener.DefaultMessageListenerContainer to manage number of consumers and provide custom listener using org.springframework.jms.listener.adapter.MessageListenerAdapter)
app-context.xml contains all spring beans listeners and other stuff, the code below is bootstrapping Spring provided listeners on queues. So idea is to use Spring classes to manage multiple consumers. Let me know if this is what you need and need more information on configuring MessageListenerAdapter.
public static void main(String[] args)
{
try
{
new ClassPathXmlApplicationContext("app-context.xml");
}
catch (Throwable e)
{
e.printStackTrace();
System.exit(-1);
}
}
The main idea is to make main thread wait until app will be finished.
while(!finished) is correct way to wait until main thread will be wake up.
finishEventHandler - is a method that handles finish/quit event.
I consider that JMS initialization is done in Spring conf. and in ". . . . ." section.
public static void main(String args[]) {
try {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
lock = new Object();
while(!finished) {
synchronized (lock) {
lock.wait();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void finishEventHandler() {
finished = true;
lock.notify();
}
In your main() do something like this:
// gather config files
String[] configs = { "classpath:applicationContext-myutil.xml" };
// create the app context
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(configs);
// obtain reference to your utility bean
MyUtilityBean utility = (MyUtilityBean) ctx.getBean("utility");
// invoke methods on your utility bean
utility.doSomething()
You would inject your Utiltity bean with Spring's JMS template to do the gruntwork for your usecase.
Attempt a read in a loop. If a message is found, process it, then sleep and iterate again. Also put some sort of terminator logic in there (interrupt the thread). You could terminate after X attempts, read from a file, kill the JVM, read a terminator msg from the queue, etc., etc.
public static void main(String[] args) {
while(true) {
// look for some terminator
// attempt to read off queue
// process message
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
break;
}
}
}

Categories