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>
Related
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.
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);
}
}
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.
This question already has an answer here:
Using special auto start servlet to initialize on startup and share application data
(1 answer)
Closed 7 years ago.
I would like to perform some action as soon as my application (Enterprise Application with Business Logic, EJB, and a Client, Web) is deployed.
For example I would like to make some entity in a persistent state, or otherwise create a file.
How can I do that?
Thanks.
Configure SerlvetContextListener and override contextInitilized()
in your web application description , web.xml
<web-app ...>
<listener>
<listener-class>com.someCompany.AppNameServletContextListener</listener-class>
</listener>
</web-app
package com.someCompany;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class AppNameServletContextListener implements ServletContextListener{
#Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("ServletContextListener destroyed");
}
#Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("ServletContextListener started");
// do the things here
}
}
The "default" way is to have a servlet with an init() method. Then in the servlet-descriptor you mark this servlet as load-on-startup 1:
Example:
<servlet-name>Seam Resource Servlet</servlet-name>
<servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
As soon as the servlet is deployed (which happens after the EJBs are deployed), that init() method is called and you can execute the task you want.
With present web application in your ear, the easiest and simplest would be to use ServletContextListener, otherwise in EJB 3.1 you could use automatic timers or startup singleton session beans.
I am using JBoss 4.2.3 with JDK 1.5. I have created a stateless EJB whose purpose is to delete a file after a specified period of time (in milliseconds).
The EJB code is:
import java.io.File;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;
import org.jboss.annotation.ejb.LocalBinding;
#Stateless
#LocalBinding(jndiBinding = "TimedFileDeletion")
public class TimedFileDeletionBean implements TimedFileDeletionBeanLocal {
#Resource
TimerService timerService;
File fileToDelete;
public void setRequiredInfo(long intervalDuration, File fileToDelete) {
timerService.createTimer(intervalDuration, "Created new timer");
this.fileToDelete = fileToDelete;
}
#Timeout
public void timeout(Timer timer) {
System.out.println("Timeout occurred");
if(fileToDelete.exists()) {
fileToDelete.delete();
}
}
}
The local interface is:
import java.io.File;
public interface TimedFileDeletionBeanLocal {
public void setRequiredInfo(long intervalDuration, File fileToDelete);
}
When I call the bean through the web container (I use the Stripes framework) the timeout method is called after the specified time but it only prints "Timeout occurred", it does not delete the file and it throws an exception. This is the console output:
INFO [STDOUT] Timeout occurred
ERROR [TimerImpl] Error invoking ejbTimeout: javax.ejb.EJBException: java.lang.NullPointerException
Any advice would be appreciated.
In stateless session bean, conversational state is not maintained. Stateless bean instance variables are shared between invocations, so they may overlap.
Therefore even if you set file using setRequiredInfo(), on timeout it gets the fileToDelete null.
Try checking null before doing operation.
Below is some code snippet might help you.
class FileUtility {
// Make singleton class to store list of files to delete
public static List<File> files;
//-- get/set accessing methods
}
//---------------------
public void setRequiredInfo(long intervalDuration, File fileToDelete) {
timerService.createTimer(intervalDuration, fileToDelete.getName()+Math.random());
FileUtility.files.add(fileToDelete);
}
//---------------------
#Timeout
public void timeout(Timer timer) {
System.out.println("Timeout occurred");
for(File fileToDelete : Fileutility.files){
if(fileToDelete.exists()) {
fileToDelete.delete();
}
}
}
One thing that appears that might be a problem is that you're passing into the setRequiredInfo method a reference to a File. That reference is then stored locally, using a debugger I would verify that the reference value is the same when the timer fires. I suspect that it may no longer be the same file referenced or that the File object may be transient.
Also, just a little warning with EJBTimers and JBoss. This version of JBoss spins a thread for every EJB with a timer. So, if you have 500 files to delete with these EJBs, JBoss will spin up 500 threads. This behavior, while undesirable, does comply with the EJB spec (which is ambiguous on implementation). These threads will be recreated if the container restarts and the timers are still waiting to fire.