36

I want to create a scheduler with Quartz 2.2 in java dynamic web application. I am new to this task. I tried all the tutorials around the web. I trying context listener method to initialize the scheduler. It doesn't seem like working. The hello world program only works in general java application. for web application its looks tricky.

pom.xml:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test.ananth</groupId>
<artifactId>test-app</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>test-app Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>servlet-api</artifactId>
        <version>6.0.30</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.16</version>
    </dependency>

    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz-jobs</artifactId>
        <version>2.2.0</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.6.6</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
    </dependency>
</dependencies>
<build>
    <finalName>test-app</finalName>
</build>

quartz.properties:

#org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

# Main Quartz configuration
org.quartz.scheduler.skipUpdateCheck = true
org.quartz.scheduler.instanceName = MyQuartzScheduler
org.quartz.scheduler.jobFactory.class = org.quartz.simpl.SimpleJobFactory
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#org.quartz.threadPool.threadCount = 5

HelloJob.java:

    package com.test;

    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;

    public class HelloJob implements Job {

public HelloJob() {
}

public void execute(JobExecutionContext context)
        throws JobExecutionException {

    System.out.println("Hello!  HelloJob is executing.");
}
    }

servlet.java package com.test;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

public class MyServlet extends HttpServlet {

    /**
     * 
     */
    private static final long serialVersionUID = 1567185871113714035L;

    public void init(ServletConfig cfg) {
        String key = "org.quartz.impl.StdSchedulerFactory.KEY";
        ServletContext servletContext = cfg.getServletContext();
        StdSchedulerFactory factory = (StdSchedulerFactory) servletContext
                .getAttribute(key);
        // Scheduler quartzScheduler = factory.getScheduler("MyQuartzScheduler");
        Scheduler sched;
        try {
            sched = factory.getScheduler("MyQuartzScheduler");
            //sched = factory.getScheduler();//MyQuartzScheduler
            sched.start();

            // define the job and tie it to our HelloJob class
            JobDetail job = newJob(HelloJob.class).withIdentity("myJob",
                    "group1").build();

            // Trigger the job to run now, and then every 40 seconds
            Trigger trigger = newTrigger()
                    .withIdentity("myTrigger", "group1")
                    .startNow()
                    .withSchedule(
                            simpleSchedule().withIntervalInSeconds(4)
                                    .repeatForever()).build();

            // Tell quartz to schedule the job using our trigger
            sched.scheduleJob(job, trigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

    }
}

Web.xml:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <context-param>
        <context-param>
            <param-name>quartz:shutdown-on-unload</param-name>
            <param-value>false</param-value>
        </context-param>
        <context-param>
            <param-name>quartz:wait-on-shutdown</param-name>
            <param-value>true</param-value>
        </context-param>
        <context-param>
            <param-name>quartz:start-scheduler-on-load</param-name>
            <param-value>true</param-value>
        </context-param>
        <context-param>
            <param-name>quartz:config-file</param-name>
            <param-value>/WEB-INF/quartz.properties</param-value>
        </context-param>

        <listener>
            <listener-class>
                org.quartz.ee.servlet.QuartzInitializerListener
            </listener-class>
        </listener>

        <listener>
            <listener-class>com.test.ApplicationStartup</listener-class>
        </listener>
        <display-name>Archetype Created Web Application</display-name>
</web-app>

I am using maven web app archtype.

CRABOLO
  • 8,605
  • 39
  • 41
  • 68
Poornan
  • 741
  • 2
  • 11
  • 25

1 Answers1

79

Contents

  • Eclipse project
  • With Maven
  • XML-Less

Eclipse project

If you are using a typical project in eclipse, the most basic example has a structure similar to:

C:.
|
+---src
|   |   log4j.dtd
|   |   log4j.xml
|   |   quartz.properties
|   |   quartz_data.xml
|   |
|   \---org
|       \---paulvargas
|           \---test
|               \---quartz
|                       TestJob.java
|
\---WebContent
    \---WEB-INF
        |   web.xml
        |
        \---lib
                jta-1.1.jar
                log4j-1.2.17.jar
                quartz-2.1.5.jar
                slf4j-api-1.6.5.jar
                slf4j-log4j12-1.6.5.jar

Where the code of each of the files is as follows:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">

    <listener>
        <listener-class>org.quartz.ee.servlet.QuartzInitializerListener</listener-class>
    </listener>

</web-app>

quartz.properties

# ----------------------------- Threads --------------------------- #
# How many jobs can run at the same time?
org.quartz.threadPool.threadCount=5

# ----------------------------- Plugins --------------------------- #
# Class to load the configuration data for each job and trigger.
# In this example, the data is in an XML file.
org.quartz.plugin.jobInitializer.class=org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin

quartz_data.xml

<?xml version="1.0" encoding="UTF-8"?>

<job-scheduling-data
    xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData http://www.quartz-scheduler.org/xml/job_scheduling_data_2_0.xsd"
    version="1.8">

    <schedule>
        <job>
            <name>TestJob</name>
            <job-class>org.paulvargas.test.quartz.TestJob</job-class>
        </job>
        <trigger>
            <cron>
                <name>TestJob</name>
                <job-name>TestJob</job-name>
                <cron-expression>0 0/5 * 1/1 * ? *</cron-expression>
            </cron>
        </trigger>
    </schedule>

</job-scheduling-data>

The job is executed every 5 minute(s) (see the expression 0 0/5 * 1/1 * ? * in the cron-expression tag). If you want another expression, you can build it with http://www.cronmaker.com/

TestJob.java

package org.paulvargas.test.quartz;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class TestJob implements Job {

    @Override
    public void execute(final JobExecutionContext ctx)
            throws JobExecutionException {

        System.out.println("Executing Job");

    }

}

log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd" >
<log4j:configuration>

    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p (%c.java:%L).%M - %m%n"/>
        </layout>
    </appender>

    <root>
        <priority value="TRACE" />
        <appender-ref ref="STDOUT"/>
    </root>

</log4j:configuration>

With Maven

If you are using Maven, the structure for the same project is:

C:.
|   pom.xml
|
\---src
    \---main
        +---java
        |   \---org
        |       \---paulvargas
        |           \---test
        |               \---quartz
        |                       TestJob.java
        |
        +---resources
        |       log4j.dtd
        |       log4j.xml
        |       quartz.properties
        |       quartz_data.xml
        |
        \---webapp
            |   index.jsp
            |
            \---WEB-INF
                    web.xml

And the file pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>test</groupId>
    <artifactId>BasicQuartz</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>

    <name>BasicQuartz</name>
    <url>http://maven.apache.org</url>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
            <version>2.2.1</version>
        </dependency>
        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

XML-less

* This requires Servet 3.0+ (Tomcat 7+, Glassfish 3+, JBoss AS 7)

You only need two files: TestJob.java from the previous example and the following listener:

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.annotation.WebListener;

import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.quartz.impl.StdSchedulerFactory;

@WebListener
public class QuartzListener extends QuartzInitializerListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        super.contextInitialized(sce);
        ServletContext ctx = sce.getServletContext();
        StdSchedulerFactory factory = (StdSchedulerFactory) ctx.getAttribute(QUARTZ_FACTORY_KEY);
        try {
            Scheduler scheduler = factory.getScheduler();
            JobDetail jobDetail = JobBuilder.newJob(TestJob.class).build();
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity("simple").withSchedule(
                    CronScheduleBuilder.cronSchedule("0 0/1 * 1/1 * ? *")).startNow().build();
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();
        } catch (Exception e) {
            ctx.log("There was an error scheduling the job.", e);
        }
    }

}

To avoid conflicts, do not set the default listener in the web.xml at the same time. With this last example, the default number of threads is 10. Since the scheduler started in stand-by mode, it is necessary to call scheduler.start();. The "simple" identity is optional, but you can use it for reschedule the job (That's great!). e.g.:

ServletContext ctx = request.getServletContext();
StdSchedulerFactory factory = (StdSchedulerFactory) ctx.getAttribute(QuartzListener.QUARTZ_FACTORY_KEY);
Scheduler scheduler = factory.getScheduler();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("simple").withSchedule(
        CronScheduleBuilder.cronSchedule(newCronExpression)).startNow().build();
Date date = scheduler.rescheduleJob(new TriggerKey("simple"), trigger);
fujy
  • 5,168
  • 5
  • 31
  • 50
Paul Vargas
  • 41,222
  • 15
  • 102
  • 148
  • 1
    Thanks. that was perfect, I just sorted out my project related issues. Thanks a lot – Poornan Oct 28 '13 at 16:32
  • 3
    one of best answers on SOF – Nicholas DiPiazza Nov 26 '14 at 03:22
  • 1
    problem for me is that class won't let me inject parameters from spring. – Nicholas DiPiazza Jan 21 '15 at 11:01
  • 2
    With Tomcat 7 I get an Error listenerStart message when adding the listener and having the jars in the server lib folder. When I move the jars to the application WEB-INF/lib it additionally produces the error: appears to have started a thread... but has failed to stop it. Is Tomcat a special case when it comes to quartz? – J E Carter II Aug 11 '15 at 18:02
  • Hello, @JECarterII Humm... If the application stops, the listener should execute this method: http://bit.ly/1h0oYfO I will do a test in a couple of hours with Tomcat 7. – Paul Vargas Aug 11 '15 at 18:09
  • @PaulVargas - I got past the error by changing the classname in the web.xml to match the package and class name of the TestJob. Not sure if this is correct, but the error is gone. Waiting to see if the TestJob executes on schedule. – J E Carter II Aug 11 '15 at 18:45
  • @PaulVargas - This is great, but if I use spring mvc, I am able to schedule tasks and execute codes from my controller class, but I am unable to run codes in any of my service classes. In this case I am always getting null pointer exceptions –  Aug 12 '15 at 03:41
  • I have explained it in detail n the following stack overflow post here [link](http://stackoverflow.com/questions/31932897/getting-null-pointer-exception-while-trying-to-call-a-service-class-method-using) –  Aug 12 '15 at 03:49
  • Hi, @edwiser :) Did you mean something like [this](http://www.concretepage.com/spring-4/spring-4-quartz-2-scheduler-integration-annotation-example-using-javaconfig)? - I will add an answer to your question... will get some time... – Paul Vargas Aug 12 '15 at 04:18
  • @PaulVargas, I am getting an exception as followss similar to the described by J E Carter II, the exception I get is : WARNING [localhost-startStop-1] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [smartwcm-admin] appears to have started a thread named [DefaultQuartzScheduler_Worker-1] but has failed to stop it. . Is there any workaround for this –  Aug 13 '15 at 17:19
  • Hi Paul, great answer. I'd point out the need to shutdown the scheduler on the condextDestroyed method of the QuartzListener, though, or the thread would remain hanged during redeploys... – Andrea Ligios Mar 22 '17 at 14:53
  • Hey, @AndreaLigios, how are you? :D Since you are extending [`QuartzInitializerListener`](http://grepcode.com/file/repo1.maven.org/maven2/org.quartz-scheduler/quartz/2.2.1/org/quartz/ee/servlet/QuartzInitializerListener.java#249), this implements the `condextDestroyed` method. ;) – Paul Vargas Mar 22 '17 at 17:11
  • For the records (and for what I've got): the original `contextDestroyed()` method tries to shutdown the internal, private `scheduler`, not the one instantiated with `Scheduler scheduler = factory.getScheduler();`, and without shutting it down manually into an overriden `contextDestroyed()` method, the Tomcat hot-redeploy gives the warning, ***WARNING: The web application [foobar] appears to have started a thread named [QuartzScheduler_Worker-1] but has failed to stop it. This is very likely to create a memory leak.*** , memory-leaking the scheduler's thread that it was unable to stop. Cheers – Andrea Ligios Mar 23 '17 at 10:40
  • 1
    @AndreaLigios Awesome! :D Excellent research and analysis. And now it's time for update the answer. Thanks for sharing these details. – Paul Vargas Mar 23 '17 at 15:56
  • @PaulVargas you want me to update the answer, or you're doing it yourself ? :) – Andrea Ligios Mar 23 '17 at 16:48
  • @AndreaLigios Don't worry! ;) I mean, I'll do it. – Paul Vargas Mar 23 '17 at 16:58
  • Thankyou for the amazingly detailed answer. Took me just 10 minutes to get this up and running. – jkb016 Apr 16 '17 at 11:12
  • This post is so good, it should be a part of the official documentation! thanks very much :) – Shay Apr 13 '18 at 11:41
  • 1
    @AndreaLigios this is great! does someone know what the cronjob expression means `("0 0/1 * 1/1 * ? *")` i.e. how often does it execute? – SyCode Jul 04 '19 at 20:15
  • @PaulVargas excellent answer with very useful resources! Good job. – SyCode Jul 06 '19 at 14:02
  • Although the discussion here makes mention of updating the example to destroy the context scheduler, I was still getting the thread error. And the older examples on SOF were using methods to do that which no longer exist. What I came up for that is the following, in an implementation of ServletContextListener.contextDestroyed(): Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.shutdown( true ); // true = wait for jobs to complete Thread.sleep(1000); // give Quartz extra time to shutdown – Fred Andrews May 24 '20 at 18:11