This question already has answers here:
How to run a background task in a servlet based web application?
(5 answers)
Closed 7 years ago.
I am not very familiar with Tomcat, in my head it is basically abstracted as a cgi server that saves the JVM between calls -- I know it can do a lot more than that, though.
I am looking for a way to launch a background thread when a Tomcat server starts, which would periodically update the Server Context (in my particular case this is a thread that listens to heartbeats from some other services and updates availability information, but one can imagine a variety of uses for this).
Is there a standard way to do this? Both the launching, and the updating/querying of the Context?
Any pointers to the relevant documentation and/or code samples would be much appreciated.
If you want to start a thread when your WAR is deployed, you can define a context listener within the web.xml:
<web-app>
<listener>
<listener-class>com.mypackage.MyServletContextListener</listener-class>
</listener>
</web-app>
Then implement that class something like:
public class MyServletContextListener implements ServletContextListener {
private MyThreadClass myThread = null;
public void contextInitialized(ServletContextEvent sce) {
if ((myThread == null) || (!myThread.isAlive())) {
myThread = new MyThreadClass();
myThread.start();
}
}
public void contextDestroyed(ServletContextEvent sce){
try {
myThread.doShutdown();
myThread.interrupt();
} catch (Exception ex) {
}
}
}
I am looking for a way to launch a background thread when a Tomcat server starts
I think you are looking for a way to launch a background thread when your web application is started by Tomcat.
This can be done using a ServletContextListener. It is registered in web.xml and will be called when your app is started or stopped. You can then created (and later stop) your Thread, using the normal Java ways to create a Thread (or ExecutionService).
Putting <load-on-startup>1</load-on-startup> in the <servlet> block in your web.xml will force your servlet's init() to happen as soon as Tomcat starts up, rather than waiting for the first request to arrive. This is useful if you want to spawn the background thread from init().
I'd just make a small change to the very detailed answer Chris gave; I would set myThread to be a Daemon thread by myThread.setDaemon(true); which will basically keep the thread active as long as you have other non-Daemon threads working which need your background thread around. When all these threads finish then your Daemon thread is stopped by the JVM and you do not need to handle it youself in contextDestroyed.
But that's just my 2 cents.
Another way if you are using spring based framework you can specify the class/thread which you want to initialize in the beans.xml. So when the tomcat starts up, beans.xml will initialize all the classes mentioned in it. You can also pass constructor arguments if required. Below is the example of the same.
beans.xml
<bean id="monitoringSvc" class="com.mypackage.MonitoringService">
<constructor-arg value="60"></constructor-arg>
</bean>
MonitoringService.java
public class MonitoringService{
private MyThread myThread;
public MonitoringService(int seconds){
myThread = new MyThread(seconds);
myThread.start();
}
}
Related
This question already has answers here:
Is there a way to run a method/class only on Tomcat/Wildfly/Glassfish startup?
(3 answers)
Closed 5 years ago.
I am trying to call a method when my webapplication starts. The purpose is to kick-off a timer that does some work at defined intervals.
how do i call a function helloworld when my jboss 7.1 web application starts up?
If you want to run some code before your web app serves any of your clients you need a ServletContextListener.
Create your listener class
import javax.servlet.*;
public class MyServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent e) {
//Call your function from the event object here
}
public void contextDestroyed(ServletContextEvent e) {
}
}
Put the class in WEB-INF/classes
Put a <listener> element in the web.xml file.
<listener>
<listener-class>
com.test.MyServletContextListener
</listener-class>
</listener>
Hope this helps.
Other then ContextListeners, you can also have a servlet in web.xml loading on startup:
<servlet>
<servlet-name>mytask</servlet-name>
<servlet-class>servlets.MyTaskServlet</servlet-class>
...
<load-on-startup>1</load-on-startup>
</servlet>
This servlet can start your task using whatever means you want, see for example this link.
But you shouldn't use that approach, imho.
Use a proven framework/lib like quartz or a similar tool. There are a lot of problems/issues in running and syncing tasks in web servers and it's better to use some proven tool than to repeat mistakes these tools already met and solved. It might take a little while to grasp but will avoid many headaches.
Jboss itself has some tooling for that purpose: scheduling and managing tasks. Never used so can't recommend.
Check out Quartz Scheduler. You can use a CronTrigger to fire at defined intervals. For example, every 5 minutes would look like this:
"0 0/5 * * * ?"
The idea is to implement the Job interface which is the task to run, schedule it using the SchedulerFactory/Scheduler, build the Job and CronTrigger and start it.
There is a very clear example here.
Use a ServletContextListener configured in your web.xml. Write the code that kicks off the timer in the contextInitialized method.
I am trying to get the FacesContext by calling FacesContext.getCurrentInstance() in the run() method of a Runnable class, but it returns null.
public class Task implements Runnable {
#Override
public void run() {
FacesContext context = FacesContext.getCurrentInstance(); // null!
// ...
}
}
How is this caused and how can I solve it?
The FacesContext is stored as a ThreadLocal variable in the thread responsible for the HTTP request which invoked the FacesServlet, the one responsible for creating the FacesContext. This thread usually goes through the JSF managed bean methods only. The FacesContext is not available in other threads spawned by that thread.
You should actually also not have the need for it in other threads. Moreover, when your thread starts and runs independently, the underlying HTTP request will immediately continue processing the HTTP response and then disappear. You won't be able to do something with the HTTP response anyway.
You need to solve your problem differently. Ask yourself: what do you need it for? To obtain some information? Just pass that information to the Runnable during its construction instead.
The below example assumes that you'd like to access some session scoped object in the thread.
public class Task implements Runnable {
private Work work;
public Task(Work work) {
this.work = work;
}
#Override
public void run() {
// Just use work.
}
}
Work work = (Work) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("work");
Task task = new Task(work);
// ...
If you however ultimately need to notify the client e.g. that the thread's work is finished, then you should be looking for a different solution than e.g. adding a faces message or so. The answer is to use "push". This can be achieved with SSE or websockets. A concrete websockets example can be found in this related question: Real time updates from database using JSF/Java EE. In case you happen to use PrimeFaces, look at
<p:push>. In case you happen to use OmniFaces, look at <o:socket>.
Unrelated to the concrete problem, manually creating Runnables and manually spawning threads in a Java EE web application is alarming. Head to the following Q&A to learn about all caveats and how it should actually be done:
Spawning threads in a JSF managed bean for scheduled tasks using a timer
Is it safe to start a new thread in a JSF managed bean?
On single instance of Tomcat I have a thread that was started when context was initialized. Something like this :
public class MyContextListener implements ServletContextListener {
private MyThread thread = null;
#Override
public void contextInitialized(ServletContextEvent sce) {
//Start thread...
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
//Stop thread...
}
}
This thread performs some important jobs in system every 10 minutes, and it was working fine.
Now I have switched to cluster of to instances of tomcat and this thread is running on two instances. I'm trying to achieve different behavior.
What I'm trying to achieve:
This thread should be running only on one instance at the time.
If first instance fails (on which thread was running), thread should be started on second instance.
I would be grateful for any hint.
What is my application logic ?
Application logic that is executed by a thread is as follows:
Read sth from DB.
Analyze DB information.
Do HTTP request to external system, if needed.
Sleep thread for another 10 minutes.
The point is: If I will have 2 instances of tomcat, only one should execute this logic
If I understand correctly you are not really allowed to start a new thread in your application when using a web-application-server. All threads must be managed by the application server.
You'll have to excuse me if I'm describing this incorrectly, but essentially I'm trying to get a service-like class to be instantiated just once at server start and to sort of "exist" in the background until it is killed off at server stop. At least from what I can tell, this is not exactly the same as a typical servlet (though I may be wrong about this). What's even more important is that I need to also be able to access this service/object later down the line.
As an example, in another project I've worked on, we used the Spring Framework to accomplish something similar. Essentially, we used the configuration XML file along with the built-in annotations to let Spring know to instantiate instances of some of our services. Later down the line, we used the annotation #Autowired to sort of "grab" the object reference of this pre-instantiated service/object.
So, though it may seem against some of the major concepts of Java itself, I'm just trying to figure out how to reinvent this wheel here. I guess sometimes I feel like these big app frameworks do too much "black-box magic" behind the scenes that I'd really like to be able to fine-tune.
Thanks for any help and/or suggestions!
Oh and I'm trying to run this all from JBoss 6
Here's one way to do it. Add a servlet context listener to your web.xml, e.g.:
<listener>
<listener-class>com.example.BackgroundServletContextListener</listener-class>
</listener>
Then create that class to manage your background service. In this example I use a single-threaded ScheduledExecutorService to schedule it to run every 5 minutes:
public class BackgroundServletContextListener implements ServletContextListener {
private ScheduledExecutorService executor;
private BackgroundService service;
public void contextInitialized(ServletContextEvent sce) {
service = new BackgroundService();
// setup single thread to run background service every 5 minutes
executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(service, 0, 5, TimeUnit.MINUTES);
// make the background service available to the servlet context
sce.getServletContext().setAttribute("service", service);
}
public void contextDestroyed(ServletContextEvent sce) {
executor.shutdown();
}
}
public class BackgroundService implements Runnable {
public void run() {
// do your background processing here
}
}
If you need to access the BackgroundService from web requests, you can access it through the ServletContext. E.g.:
ServletContext context = request.getSession().getServletContext();
BackgroundService service = (BackgroundService) context.getAttribute("service");
Have you considered using an EJB 3.1 Session bean? These can be deployed in a war file, and can be annotated with #Singleton and #Startup.
A number of annotations available with EJB 3.1 are designed to bring Spring goodies into the Java EE framework. It may be the re-invention you're considering has been done for you.
If you must roll your own, you can create a servlet and configure it start up when the application does using load-on-startup. I built a system like that a few years ago. We then used the new(ish) java.util.concurrent stuff like ExecutorService to have it process work from other servlets.
More information about what you're trying to do, and why the existing ways of doing things is insufficient, would be helpful.
You can use messaging for that. Just send message to the queue, and let the message listener do the processing asynchronously in the background.
You can use JMS for the implementation, and ActiveMQ for the message broker.
Spring has JMSTemplate, JMSGateWaySupport API to make JMS Implementation simple
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jms.html
I need a very simple process that listens on a directory and
does some operation when a new file is created on that directory.
I guess I need a thread pool that does that.
This is very easy to implement using the spring framework, which I normally use but I can't use it now.
I can only use tomcat, How can I implement it? what is the entry point that "starts" that thread?
Does it have to be a servlet ?
thanks
since you refined the question, here comes another answer: how to start a daemon in tomcat:
first, register your Daemons in web.xml:
< listener >
my.package.servlet.Daemons
< /listener >
then implement the Daemons class as an implementation of ServletContextListener like this:
the code will be called every 5 seconds, tomcat will call contextDestroyed when your app shuts down. note that the variable is volatile, otherwise you may have troubles on shutdown on multi-core systems
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class Daemons implements ServletContextListener {
private volatile boolean active = true;
Runnable myDeamon = new Runnable() {
public void run() {
while (active) {
try {
System.out.println("checking changed files...");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
public void contextInitialized(ServletContextEvent servletContextEvent) {
new Thread(myDeamon).start();
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
active = false;
}
}
You could create a listener to start the thread, however this isn't a good idea. When you are running inside a Web container, you shouldn't start your own threads. There are a couple of questions in Stack Overflow for why is this so. You could use Quartz (a scheduler framework), but I guess you couldn't achieve an acceptable resolution.
Anyway, what you are describing isn't a Web application, but rather a daemon service. You could implement this independently from your web application and create a means for them to communicate with each other.
true java-only file notifiaction will be added in java 7. here is a part of the javadoc that describes it roughly.
The implementation that observes events from the file system is intended to map directly on to the native file event notification facility where available
right now you will have to either create a native platform-dependent program that does that for you,
or alternatively implement some kind of polling, which lists the directory every so often to detect changes.
there is a notification library that you can use right now - it uses a C program on linux to detect changes over at sourceforge. on windows it uses polling. i did not try it out to see if it works.