How to trigger a Quartz job manually (JSF 2 example)
In Quartz, you can trigger a job manually via following pattern :
JobKey jobKey = new JobKey(jobName, jobGroup);
scheduler.triggerJob(jobKey); //trigger a job by jobkey
In this tutorial, we will show you a JSF 2 web application, display all the Quartz jobs on dataTable
, and allow user to click a link o fire the job manually.
Tool used :
- JSF 2.1.11
- Quartz 2.1.5
- Eclipse 4.2
- Maven 3
- Tested on Tomcat 6 and 7
1. Project Directory
Final project directory.
2. Project Dependency
All dependencies of this tutorial.
File : pom.xml
<project ...>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.11</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.11</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
</dependency>
<!-- solve method not found error in tomcat -->
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>com.sun.el</groupId>
<artifactId>el-ri</artifactId>
<version>1.0</version>
</dependency>
<!-- Quartz scheduler framework -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
</project>
3. Quartz Jobs
Create two Jobs and integrate with JSF , via listener in web.xml
.
package com.mkyong.jobs;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class JobA implements Job {
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
System.out.println("Job A is runing");
}
}
package com.mkyong.jobs;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class JobB implements Job {
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
System.out.println("Job B is runing");
}
}
File : quartz.properties
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
org.quartz.plugin.jobInitializer.class =org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
org.quartz.plugin.jobInitializer.fileNames = quartz-config.xml
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
File : quartz.config
<?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_1_8.xsd"
version="1.8">
<schedule>
<job>
<name>JobA</name>
<group>GroupDummy</group>
<description>This is Job A</description>
<job-class>com.mkyong.jobs.JobA</job-class>
</job>
<trigger>
<cron>
<name>dummyTriggerNameA</name>
<job-name>JobA</job-name>
<job-group>GroupDummy</job-group>
<!-- It will run every 30 seconds -->
<cron-expression>0/30 * * * * ?</cron-expression>
</cron>
</trigger>
</schedule>
<schedule>
<job>
<name>JobB</name>
<group>GroupDummy</group>
<description>This is Job B</description>
<job-class>com.mkyong.jobs.JobB</job-class>
</job>
<trigger>
<cron>
<name>dummyTriggerNameB</name>
<job-name>JobB</job-name>
<job-group>GroupDummy</job-group>
<!-- It will run every 30 seconds -->
<cron-expression>0/30 * * * * ?</cron-expression>
</cron>
</trigger>
</schedule>
</job-scheduling-data>
File : 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">
<display-name>JavaServerFaces</display-name>
<!-- Change to "Production" when you are ready to deploy -->
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<!-- Welcome page -->
<welcome-file-list>
<welcome-file>faces/welcome.xhtml</welcome-file>
</welcome-file-list>
<!-- JSF mapping -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map these files with JSF -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<listener>
<listener-class>
org.quartz.ee.servlet.QuartzInitializerListener
</listener-class>
</listener>
</web-app>
4. JSF Bean
A JSF bean to provide data for dataTable
later. In constructor, get all existing jobs and add into a List, and return it.
package com.mkyong.scheduler;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
@ManagedBean(name = "scheduler")
@SessionScoped
public class SchedulerBean implements Serializable {
private static final long serialVersionUID = 1L;
private Scheduler scheduler;
private List<QuartzJob> quartzJobList = new ArrayList<QuartzJob>();
public SchedulerBean() throws SchedulerException {
ServletContext servletContext = (ServletContext) FacesContext
.getCurrentInstance().getExternalContext().getContext();
//Get QuartzInitializerListener
StdSchedulerFactory stdSchedulerFactory = (StdSchedulerFactory) servletContext
.getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY);
scheduler = stdSchedulerFactory.getScheduler();
// loop jobs by group
for (String groupName : scheduler.getJobGroupNames()) {
// get jobkey
for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher
.jobGroupEquals(groupName))) {
String jobName = jobKey.getName();
String jobGroup = jobKey.getGroup();
// get job's trigger
List<Trigger> triggers = (List<Trigger>) scheduler
.getTriggersOfJob(jobKey);
Date nextFireTime = triggers.get(0).getNextFireTime();
quartzJobList.add(new QuartzJob(jobName, jobGroup, nextFireTime));
}
}
}
//trigger a job
public void fireNow(String jobName, String jobGroup)
throws SchedulerException {
JobKey jobKey = new JobKey(jobName, jobGroup);
scheduler.triggerJob(jobKey);
}
public List<QuartzJob> getQuartzJobList() {
return quartzJobList;
}
public static class QuartzJob {
private static final long serialVersionUID = 1L;
String jobName;
String jobGroup;
Date nextFireTime;
public QuartzJob(String jobName, String jobGroup, Date nextFireTime) {
this.jobName = jobName;
this.jobGroup = jobGroup;
this.nextFireTime = nextFireTime;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getJobGroup() {
return jobGroup;
}
public void setJobGroup(String jobGroup) {
this.jobGroup = jobGroup;
}
public Date getNextFireTime() {
return nextFireTime;
}
public void setNextFireTime(Date nextFireTime) {
this.nextFireTime = nextFireTime;
}
}
}
5. JSF Pages
A web page, get all existing jobs via EL #{scheduler.quartzJobList}
, and display it via dataTable
component. When the “fireNow” link is clicked, it will fire the specified job immediately.
File : welcome.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<h:outputStylesheet library="css" name="table-style.css" />
</h:head>
<h:body>
<h1>All Quartz Jobs</h1>
<h:form>
<h:dataTable value="#{scheduler.quartzJobList}" var="quartz"
styleClass="quartz-table" headerClass="quartz-table-header"
rowClasses="quartz-table-odd-row,quartz-table-even-row">
<h:column>
<!-- column header -->
<f:facet name="header">Job Name</f:facet>
<!-- row record -->
#{quartz.jobName}
</h:column>
<h:column>
<f:facet name="header">Job Group</f:facet>
#{quartz.jobGroup}
</h:column>
<h:column>
<f:facet name="header">Next Fire Time</f:facet>
<h:outputText value="#{quartz.nextFireTime}">
<f:convertDateTime pattern="dd.MM.yyyy HH:mm" />
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
<h:commandLink value="Fire Now"
action="#{scheduler.fireNow(quartz.jobName, quartz.jobGroup)}" />
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
File : table-style.css
.quartz-table{
border-collapse:collapse;
}
.quartz-table-header{
text-align:center;
background:none repeat scroll 0 0 #E5E5E5;
border-bottom:1px solid #BBBBBB;
padding:16px;
}
.quartz-table-odd-row{
text-align:center;
background:none repeat scroll 0 0 #FFFFFFF;
border-top:1px solid #BBBBBB;
padding:20px;
}
.quartz-table-even-row{
text-align:center;
background:none repeat scroll 0 0 #F9F9F9;
border-top:1px solid #BBBBBB;
padding:20px;
}
6. Demo
See final output. http://localhost:8080/JavaServerFaces/
how to give it a value in config file to stop
There is a typo in your CSS code. White is #FFFFFF, not #FFFFFFF (6xF not 7xF). I suspect that Eclipse doesn’t support CSS so it fails to detect this simple mistake.
Hi
Can you explain one example with spring app as JMX server with quartz
Really great example …
How to do this in spring mvc,please explain the flow,
thank you
Really great example …
How to do this in spring mvc,please explain the flow
thank you
Why my web Tomcat container startup, the JobA quartz run automatically ?
Because you have:
dummyTriggerNameA
JobA
GroupDummy
0/30 * * * * ?
configured in your quartz.config 🙂
GRAVE: Erreur lors de la configuration de la classe d’écoute de l’application (application listener) org.quartz.ee.servlet.QuartzInitializerListener
java.lang.ClassNotFoundException: org.quartz.ee.servlet.QuartzInitializerListener
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1680)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1526)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4148)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4704)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
at org.apache.catalina.core.StandardService.start(StandardService.java:525)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:754)
at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
22 mars 2013 16:29:05 org.apache.catalina.core.StandardContext listenerStart
GRAVE: L’installation des écouteurs (listeners) de l’application a été sautée suite aux erreurs précédentes
22 mars 2013 16:29:05 org.apache.catalina.core.StandardContext start
GRAVE: Error listenerStart
22 mars 2013 16:29:05 org.apache.catalina.core.StandardContext start
GRAVE: Erreur de démarrage du contexte [/JavaServerFaces] suite aux erreurs précédentes
22 mars 2013 16:29:05 org.apache.coyote.http11.Http11Protocol start
INFO: Démarrage de Coyote HTTP/1.1 sur http-8080
22 mars 2013 16:29:05 org.apache.jk.common.ChannelSocket init
INFO: JK: ajp13 listening on /0.0.0.0:8009
22 mars 2013 16:29:05 org.apache.jk.server.JkMain start
INFO: Jk running ID=0 time=0/31 config=null
22 mars 2013 16:29:05 org.apache.catalina.startup.Catalina start
INFO: Server startup in 751 ms
————Please help
can anyone help me ?
I have a custom JobStore implementation set in quartz.properties. But everytime I run index.xhtml, it takes the default RAMJobStore implementation. When I run the scheduler bean with main class, it takes my custom implementation, but takes RAM job store when I run through the index.xhtml through the tomcat server. Does anybody have any idea why?
Really great example..
but i need get scheduler settings from database..
like when the apps runs job based on date and time server. and those settings saved in database.
any links or example to guide me there.. 😀
thanks.
Great example. Can you make an example about start and stop Job manual?
Thanks for your quick action, thanks alot.
Jimmy
Really great example …
Thanks.
You code throws a NullPointerException with JSF 2.2.