Executing task after deployment of Java EE application - java

I have a Java EE application which should start a synchronization process with an external system once after its deployment.
How could I implement this requirement?

Below are listed a couple of popular methods for getting lifecycle callbacks in JavaEE apps.
Create a javax.servlet.ServletContextListener implementation
If you have a web component to your .ear file (embedded .war) or your deployment is a .war by itself you can add a ServletContextListener to your web.xml and get a callback when the server starts or is shutting down.
Example:
package com.stackoverflow.question
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
public class MyServletContextListener implements ServletContextListener{
#Override
public void contextInitialized(ServletContextEvent contextEvent) {
/* Do Startup stuff. */
}
#Override
public void contextDestroyed(ServletContextEvent contextEvent) {
/* Do Shutdown stuff. */
}
}
and then add this configuration to your web.xml deployment descriptor.
$WAR_ROOT/WEB-INF/web.xml.
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee">
<listener>
<listener-class>com.stackoverflow.question.MyServletContextListener</listener-class>
</listener>
</web-app>
Create an EJB 3.1 #Startup Bean
This method uses an EJB 3.1 singleton to get a startup and shutdown callback from the server.
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Startup;
import javax.ejb.Singleton;
#Singleton
#Startup
public class LifecycleBean {
#PostConstruct
public void init() {
/* Startup stuff here. */
}
#PreDestroy
public void destroy() {
/* Shutdown stuff here */
}
}

I tested the suggested solution which uses the #Startup and #PostConstruct annotations. It turned out that Glassfish does not complete the deployment of an application until all methods annotated with #PostConstruct have finished. So in my case the deployment would take from several minutes up to an hour.
But I figured out a different way to achive what I want. The best solution seems to be a timer callback method which cancels its timer after its execution.
#Stateless
public class SynchronisationService {
#Schedule(hour = "*", minute = "*", persistent = false)
protected void init(Timer timer)
{
doTheSync();
timer.cancel();
}
}
Using a non-persistent timer allows the timer to be re-created if the application server is restarted.

You can use the #Startup and #PostConstruct annotations to perform tasks on application startup.

Using a ServletContextListener, or a servlet that is initialized at startup, for example. Of course, this becomes much harder if you have multiple deployments of the application in a cluster, and only want this process to be run once.

Related

Why #PreDestory not working on websocket #ServerEndpoint?

I log some message to see the lifecycle of #ServerEndpoint. PostConstruct, onOpen, onClose are triggered in turns, but #PreDestroy is never triggered. Do I miss something?
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.websocket.OnClose;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
#ServerEndpoint("/")
public class ServerWebSocket {
#PostConstruct
public void initialize() {
System.out.println("PostConstruct");
}
#OnOpen
public void open(Session session) {
System.out.println("OnOpen");
}
#OnClose
public void close(Session session) {
System.out.println("OnClose");
}
#PreDestroy
public void destroy() {
System.out.println("PreDestroy");
}
}
I use GlassFish4 with bean.xml in WEB-INF
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1" bean-discovery-mode="all">
</beans>
#OnOpen and #OnClose callbacks are triggered by the lifecycle of every WebSocket session.
There is no any CDI scope specified, which could be used with WebSocket endpoints. And this is ok, because it has it's own session.
So, why is #PostConstruct triggered? WebSocket specification says, that:
Websocket endpoints running in the Java EE platform must have full dependency injection support as described in the CDI specification.
And #PostConstruct API Doc points:
The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. ... This annotation MUST be supported on all classes that support dependency injection.
Why #PreDestroy then doesn't work? I think, Websocket endpoints behave like singletons, and #PreDestroy will be triggered during removing of the instance, which will occur just before application stop. You could check it in your environment by stopping application from the GlassFish administration console.

Run code after Spring context has been initialized in web app deployed on Tomcat

I have a web application which I package as a war and I deploy on tomcat 7.
This is a Spring MVC application that I want to add a Quartz scheduler to it. I want to run code that will make use of various Spring beans and populates the scheduler with Jobs that are configured dynamically so I need access to various Beans in code like
#Autowired
private CronDAO cronDAO;
public void loadCronJobs(final Scheduler scheduler) {
LOGGER.info("Vas[{}]: Loading Cron Schedule...", vasName);
final List<Cron> crons = cronDAO.fetchAll();
final Long cronSid = cron.getSid();
final String directive = cron.getDirective();
final String expression = cron.getCronExpression();
...
}
Normally I would put the initialization code for the scheduler in the main function of the application and would use Autowired beans for my application logic. Now with the application server initializing the application I cannot do this.
I tried to add my code to the startup function of the servlet but the Spring context is not yet ready so the autowired beans (the DAO in this example) fail to load.
public class MyWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
CronManager cron = new CronManagerImpl();
cron.loadCronJobs();
}
}
Is there a way to run code (not only quartz related) when the application start in the container but the Spring context to be complete?
You can write a 'ServletContextListener' and register it in the web.xml. In your implementation you can rely on spring frameworks WebApplicationContextUtil to get a handle to the spring application context.
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class QuartzInitiator implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContextEvent.getServletContext())
.getAutowireCapableBeanFactory().autowireBean(this);
//your logic
}
}
Autowired fields would be correctly initialized by the Util.
Spring is great at avoiding you do deal with low level questions.
At higher level you have :
a number of beans declared in Spring application context
an initialization procedure that must be run
after the beans have been initialized
before the rest of code is run
Well simply declare a new bean in which you inject all the other beans, and do your initialization in the init-method of that last bean. Spring guarantees that this method is called at context initialization time after all dependant beans have been initialized.
That way is totally independant of whether your application is a web application or not : is becomes spring framework's problem and no longer yours.

Tomcat application deployment listener

I'm wondering how can I listen for Tomcat web application deployments. I would like to have my listener invoked every time an application is undeployed or deployed from/to the container.
I already investigate a bit and found out that some listeners, i.e. LifecycleListener can be registered over JMX. But unfortunatelly this listener ins't enough for me since it triggers events just when Engine/Host/Context is in shutdown or startup process.
The same with ContainerListener that basically informs container shutdown and startup events.
So, my question basically is: which interface shall I implement and how can I register it to tomcat in order to be notified every time a new application is deployed?
servlet context init/destroy
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
public class AppContextListener implements ServletContextListener {
private static final Log logger = LogFactory.getLog(AppContextListener.class);
#Override
public void contextDestroyed(ServletContextEvent e) {
logger.warn("AppContext Delete: " + e.getServletContext().getContextPath());
}
#Override
public void contextInitialized(ServletContextEvent e) {
logger.warn("AppContext Create: " + e.getServletContext().getContextPath());
}
}
and put into tomcat/conf/web.xml
<listener>
<listener-class>AppContextListener</listener-class>
</listener>

Server side timer in a JSF-2 application

in the JSF-2 application I'm working on, I need to start a server side Timer when a user does an action.
This timer must be related to the application itself, so it must survive when the user session is closed.
To solve this problem, I thought to use java.util.Timer class instantiating the timer object in an Application scoped bean.
Could it be a good solution? Are there other better ways to achive this? Thanks
No ejb-container
If your container doesnt have ejb capabilities (tomcat, jetty etc..), you can go with quartz scheduler library: http://quartz-scheduler.org/
They also has some nice code samples: http://quartz-scheduler.org/documentation/quartz-2.1.x/examples/Example1
EJB 3.1
If your app-server have a EJB 3.1 (glassfish, Jboss), there is a java ee standard way of creating timers. Mainly look into the #Schedule and #Timeout annotations.
Something like this might cover your usecase (method annotated #Timeout will be invoked when timer runs out)
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
#Stateless
public class TimerBean {
#Resource
protected TimerService timerService;
#Timeout
public void timeoutHandler(Timer timer) {
String name = timer.getInfo().toString();
System.out.println("Timer name=" + name);
}
public void startTimer(long initialExpiration, long interval, String name){
TimerConfig config = new TimerConfig();
config.setInfo(name);
config.setPersistent(false);
timerService.createIntervalTimer(initialExpiration, interval, config);
}
}

Java scheduling service

I'm needing to make a service that schedules jobs that are basically get requests that hit some servlet. I tried to do this w/ a servlet context listener based on this post, Running a background Java program in Tomcat, but the web.xml changes that were defined are causing 404 errors on the Tomcat server. Does anyone have any other suggestions on how to accomplish this?
One idea I have at this point is to define a runnable servlet
public class Service extends HttpServlet implements Runnable {
//Does stuff
init() {
new Thread(this);
}
}
Is this a reasonable approach?
you can look into using quartz scheduler for jobs :
http://quartz-scheduler.org/
for instance (not specific to your task):
import java.util.Map;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class SchedulerJob implements Job
{
public void execute(JobExecutionContext context)
throws JobExecutionException {
Map dataMap = context.getJobDetail().getJobDataMap();
SchedulerTask task = (SchedulerTask)dataMap.get("schedulerTask");
task.printSchedulerMessage();
}
}
Another option (for a quick turaround) would be to just use a cron job or windows task manager depending on your OS.

Categories

Resources