This section includes sample code and configuration files for using the Geronimo Quartz plugins.
The example job discussed in this section:
Reads a file
Inserts data from the file into a database
Sends an e-mail notifying someone that the data was received.
Using the embedded scheduler, you can write a standard Quartz job like this. Note that the job must create its own connection to the database and its own JavaMail session.
Job Code
public class FileDBJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { // Locate input File source = new File(...); if(!source.exists()) { return; } // Write to database Connection con = DriverManager.getConnection("url", "user", "password"); PreparedStatement ps = con.prepareStatement(...); ... con.close(); // Send e-mail Properties props = new Properties(); props.put("mail.smtp.host", smtpServer); ... Session session = Session.getDefaultInstance(props, null); Message msg = new MimeMessage(session); ... Transport.send(msg); } }
To deploy the job, you need to:
Add the job classes to the component that will schedule the job
Access the embedded Scheduler
Schedule the job by calling the
Scheduler.scheduleJob
method
The code to do the last two steps looks like this. It could be executed in a startup servlet, or on demand by a web or EJB component, etc.
Component that Schedules a Job
Context ctx = new InitialContext(); QuartzScheduler gbean = ctx.lookup("java:comp/env/Scheduler"); Scheduler scheduler = gbean.getNativeScheduler(); JobDetail detail = new JobDetail("name", "group", MyJob.class); Trigger trigger = ...; // set the schedule for the job scheduler.scheduleJob(detail, trigger);
In order for the JNDI lookup to access the scheduler GBean, the component should include a gbean-ref element in its deployment plan (e.g. geronimo-web.xml or openejb-jar.xml):
GBean Reference in Geronimo Plan
<gbean-ref> <ref-name>Scheduler</ref-name> <ref-type>org.gplugins.quartz.QuartzScheduler</ref-type> <pattern> <name>QuartzScheduler</name> </pattern> </gbean-ref>
Note that the ref-name
defines the JNDI
location where the QuartzScheduler
GBean will be
bound (java:comp/env/
plus the ref-name value -- or
java:comp/env/Scheduler
in this case, which is where
the sample code above looks it up).
Q: Is running Quartz in Geronimo this way any different from running Quartz standalone?
A: Not really. The plugin uses a Geronimo thread pool instead of the native Quartz thread pool by default. But the jobs are the same and the same configuration options are available.
Q: How do I customize the scheduler configuration?
A: Once you've installed the
plugin, an entry for the quartz-scheduler
module
will be written to var/config/config.xml
. You can
stop Geronimo and edit the Quartz settings in that file. Look at the
value for the quartzProperties
attribute, which is
in the standard Java Properties file syntax. You can add any Quartz
properties you like there, and they will override the Quartz
defaults.
Q: Can I schedule or delete jobs as part of a transaction?
A: Probably, but we haven't tried this before. We'd be happy to help you get this running if you have occasion to try.
With the deployable jobs approach, the sample job can be packaged in a JAR on its own, with its classes and schedule, and can be configured to access a database pool and mail session already deployed in Geronimo (rather than manually configuring connectivity in the job). The same job would look like this:
Job Code
public class FileDBJob implements Job { private DataSource dataSource; private Session mailSession; public void setDatabase(DataSource ds) { dataSource = ds; } public void setMailSession(Session s) { mailSession = s; } public void execute(JobExecutionContext context) throws JobExecutionException { // Locate input File source = new File(...); if(!source.exists()) { return; } // Write to database Connection con = dataSource.getConnection(); ... con.close(); // Send e-mail Message msg = new MimeMessage(mailSession); ... Transport.send(msg); } }
Notice that something must call setDatabase and setMailSession on the job before it can be executed. We'll configure the job to use a particular database pool and mail session already configured in Geronimo, and the plugin will ensure that those properties are injected into the job before running the job. The job code itself doesn't need any particular settings (database connection settings or mail server settings), which means not only are those settings more secure, but also the job won't need to change as frequently and will be more portable across different server instances.
To configure the job schedule and resources, an XML deployment
plan must be included with the JAR containing the job classes. It can be
packaged into the JAR at the location
META-INF/geronimo-quartz.xml
, or provided as a
separate argument in addition to the JAR when deploying the job.
geronimo-quartz.xml
<?xml version="1.0" encoding="UTF-8"?> <jobs xmlns="http://geronimo.apache.org/xml/ns/plugins/quartz-0.2"> <environment xmlns="http://geronimo.apache.org/xml/ns/deployment-1.1"> <moduleId> <artifactId>FileDBJob</artifactId> </moduleId> <dependencies> <dependency> <groupId>example</groupId> <artifactId>javamail-server</artifactId> </dependency> <dependency> <groupId>geronimo</groupId> <artifactId>system-database</artifactId> </dependency> </dependencies> </environment> <job> <job-name>Load File into Database</job-name> <job-class>com.example.FileDBJob</job-class> <cron-expression>0 0 23 * * ?</cron-expression> <resource-ref> <property>Database</property> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> <pattern> <name>SystemDatasource</name> </pattern> </resource-ref> <resource-ref> <property>MailSession</property> <res-type>javax.mail.Session</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> <pattern> <name>MailServer</name> </pattern> </resource-ref> </job> </jobs>
This looks similar to other Geronimo deployment plans, except the meaty part defines Quartz jobs instead of servlets or EJBs. Some important parts to note include:
The moduleId
element defines the unique
identifier for the deployed job. This name will be used as an
argument to the deploy tool to start, stop, or undeploy the job, for
example. In this case it is set to FileDBJob.
The dependencies
element lists other
modules that the job depends on. In this case, it depends on the
modules containing the JavaMail configuration and the database pool
configuration -- the two resources in the server that the job
requires.
Within a job definition, the job-name
sets
the name that will be used to identify this job to Quartz. In this
case, it is Load File into
Database.
The job-class
defines the fully-qualified
Java class name of the job. This job class should be included in the
job JAR. In this case it is com.example.FileDBJob.
The cron-expression defines a cron-based schedule for the job. In this case, the expression "0 0 23 * * ?" means that the job should run every day at 11 PM.
A job may include ejb-ref
,
resource-ref
, and gbean-ref
elements to inject various components into the job. These look
similar to the same elements in J2EE and Geronimo deployment plans,
except instead of a JNDI location used to identify them, they use a
property
-- the name of the JavaBean property
that holds the setting in question. Here the properties Database and MailSession mean the resources will be
assigned by calling setDatabase
and
setMailSession
respectively.
The pattern/name elements point to the resources by name, in the modules listed as dependencies in the dependency section. So in this case, we're looking for a JDBC DataSource called SystemDatasource and a JavaMail Session called MailServer in the parent modules named javamail-server and system-database.
If both the job class and deployment plan are packaged into a JAR, the JAR contents might look like this:
> jar -tf file-db-job.jar META-INF/ META-INF/MANIFEST.MF META-INF/geronimo-quartz.xml com/ com/example/ com/example/FileDBJob.class
Q: What's wrong with just accessing the scheduler directly to deploy jobs?
A: Nothing's "wrong" with that, but there are some advantages to the deployable jobs approach. If you manually deploy the jobs, for example, with a startup servlet, then the classes need to be bundled with your application and the application needs to be redeployed in order to update the jobs. Also, the jobs would need to manually look up or create any resources they need such as database connections or EJBs, whereas with deployable jobs those resources can be provided to the job.
Q: Does the job need to have any special code?
A: The job implements the same
org.quartz.Job
interface as any Quartz job. It
also needs to have setter methods for any resources configured for the
job. But there are no special base classes or interfaces in order for
the job to be deployed in this way.
Q: Isn't this an awful lot of XML?
A: It is more than we'd prefer. We'll look at how to reduce it in the future.
Q: Do I need a separate JAR and XML for every job?
A: No, you can list one or many jobs in each module you deploy. However, it's only possible to redeploy the module as a whole. So the benefit of deploying the jobs individually is you can update them individually, but it requires more JARs (or at least more XML plans). Each job can be started and stopped individually in any case; the only difference is redeployment.
Q: Can I configure Quartz to save jobs to a database?
A: There's normally no need to; each time you restart the server, the deployed jobs are scheduled again. If you have a use case where saving to the database is important, please let us know.
Q: I got the error
"Error: Unable to distribute my-jobs.jar: Cannot deploy the
requested application module because no deployer is able to handle
it...
"
A: That means Geronimo didn't
link up the JAR you deployed with the Quartz deployer module. First,
check that the namespace is correct in your XML plan. The jobs element
should have
xmlns="http://geronimo.apache.org/xml/ns/plugins/quartz-0.2"
(make sure that is exact, though it may change for future releases of
the plugin). If that's correct, make sure the
quartz-deployer
module is running. Try
java -jar bin/deployer.jar list-modules
and make
sure that you see gplugins/quartz-dpeloyer/.../car
in the list and that it has a +
next to it. If not,
you can run java -jar bin/deployer.jar start
quartz-deployer
to start it.