This project allows you to use a key/value store as a Quartz JobStore. See Quartz tutorial Job Stores for more information. The Framework is already implemented for the data grids Infinispan and Hazelcast. Both implementations have been deployed and tested in a JBoss EAP-6.3.0 environment but may be used in any environment.
Documentation of
InfinispanJobStore v1.0
HazelcastJobStore v1.0
ClusterCacheJobStore v1.0
Author Felix Finkbeiner
Company EXXETA AG
!!! HANDLE WITH CAUTION !!!
This documentation only describes how to use the InfinispanJobStore within a JBoss EAP environment.
Configure four Infinispan caches in your JBoss configuration file (standalone.xml) like so:
<cache-container name="jobStoreCaches" jndi-name="java:jboss/infinispan/jobStore" start="EAGER">
<transport lock-timeout="60000"/>
<replicated-cache name="cacheOne" mode="SYNC">
<locking isolation="READ_COMMITTED"/>
<transaction mode="NON_XA" locking="PESSIMISTIC"/>
<eviction strategy="NONE"/>
</replicated-cache>
<replicated-cache name="cacheTwo" mode="SYNC">
<locking isolation="READ_COMMITTED"/>
<transaction mode="NON_XA" locking="PESSIMISTIC"/>
<eviction strategy="NONE"/>
</replicated-cache>
<replicated-cache name="cacheThree" mode="SYNC">
<locking isolation="READ_COMMITTED"/>
<transaction mode="NON_XA" locking="PESSIMISTIC"/>
<eviction strategy="NONE"/>
</replicated-cache>
<replicated-cache name="cacheFour" mode="SYNC">
<locking isolation="READ_COMMITTED"/>
<transaction mode="NON_XA" locking="PESSIMISTIC"/>
<eviction strategy="NONE"/>
</replicated-cache>
</cache-container>
Configure JGroups to use udp and choose your network settings:
<subsystem xmlns="urn:jboss:domain:jgroups:1.1" default-stack="udp">
<stack name="udp">
<transport type="UDP" socket-binding="jgroups-udp"/>
<protocol type="PING"/>
<protocol type="MERGE3"/>
<protocol type="FD_SOCK" socket-binding="jgroups-udp-fd"/>
<protocol type="FD"/>
<protocol type="VERIFY_SUSPECT"/>
<protocol type="pbcast.NAKACK"/>
<protocol type="UNICAST2"/>
<protocol type="pbcast.STABLE"/>
<protocol type="pbcast.GMS"/>
<protocol type="UFC"/>
<protocol type="MFC"/>
<protocol type="FRAG2"/>
<protocol type="RSVP"/>
</stack>
</subsystem>
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
<socket-binding name="management-native" interface="management" port="${jboss.management.native.port:9999}"/>
<socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
<socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9443}"/>
<socket-binding name="ajp" port="8009"/>
<socket-binding name="http" port="8080"/>
<socket-binding name="https" port="8443"/>
<socket-binding name="jgroups-mping" port="0" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
<socket-binding name="jgroups-tcp" port="7600"/>
<socket-binding name="jgroups-tcp-fd" port="57600"/>
<socket-binding name="jgroups-udp" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
<socket-binding name="jgroups-udp-fd" port="54200"/>
<socket-binding name="remoting" port="4447"/>
<socket-binding name="txn-recovery-environment" port="4712"/>
<socket-binding name="txn-status-manager" port="4713"/>
<outbound-socket-binding name="mail-smtp">
<remote-destination host="localhost" port="25"/>
</outbound-socket-binding>
</socket-binding-group>
Within your project create the quartz.properties file under
src/main/webapp/WEB-INF/classes/quartz.properties
This file will tell Quartz what options and which JobStore it should use. Assign:
-
The names of the previously created Infinispan caches
-
The JNDI of your Infinispan container
-
The full qualifying class name of the InfinispanJobStore
-
Additional configurations of your scheduler visit Quartz configuration for more information
Note that we use the names of the caches and the JNDI that we declared under 1.1.1
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 2
org.quartz.threadPool.threadPriority: 5
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold: 60000
org.quartz.jobStore.class=com.exxeta.quartzscheduler.infinispan.InfinispanJobStore
org.quartz.jobStore.infinispanJNDI: java:jboss/infinispan/jobStore
org.quartz.jobStore.infinispanJobStoreCacheJobsByKey: cacheOne
org.quartz.jobStore.infinispanJobStoreCacheTriggersByKey: cacheTwo
org.quartz.jobStore.infinispanJobStoreCacheCalendarsByName: cacheThree
org.quartz.jobStore.infinispanJobStoreCacheMetaData: cacheFour
Create a Quartz scheduler within your application by using org.quartz.impl.StdSchedulerFactory()
@Singleton
public class SchedulerBean {
private static final Logger LOGGER = Logger.getLogger(SchedulerBean.class);
@Resource(lookup="java:jboss/infinispan/jobStore")
private CacheContainer container;
private Scheduler scheduler;
@PostConstruct
public void setupScheduler() {
SchedulerFactory schedulerFactory = new org.quartz.impl.StdSchedulerFactory();
try {
scheduler = schedulerFactory.getScheduler();
LOGGER.info("-------------Starting Scheduler---------------");
scheduler.start();
} catch (SchedulerException e) {
LOGGER.error(e.getMessage());
}
}
public Scheduler getScheduler(){
return scheduler;
}
}
Run your application and schedule some jobs.
To tell Quartz that it should use the HazelcastJobStore create the quartz.properties file usual under
src/main/webapp/WEB-INF/classes/quartz.properties
For a functioning JobStore you only have to assign the full qualifying class name of the HazelcastJobStore and configure your scheduler, visit Quartz configuration for more information.
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 2
org.quartz.threadPool.threadPriority: 5
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold: 60000
org.quartz.jobStore.class=com.exxeta.jobstore.hazelcast.HazelcastJobStore
If you want to specify your Hazelcast settings e.g. change the port, WAN, ... you can use a configuration XML.
org.quartz.jobStore.hazelcastConfigurationXML: C:\\hazelcastconfiguration.xml
If you want to create a JobStore for a key/value store other than Infinispan you can use the ClusterCacheJobStore for that. You need to provide:
-
Your own CacheConnector
-
A listener for topology changes in the cluster
-
Initialization of the CacheConnector in the ClusterCacheJobStore
See the com.exxeta.jobstore.infinispan package as example:
The CacheConnector interface is used to separate the key/value store access from the JobStore logic. You need to implement this interface for the key/value store you want to use. Your CacheConnector provides all the data grid specific operations like:
-
get, put, remove, contains,...
-
getNodeID() a identifier that represents the node in the cluster
-
Locks
-
Start-, commit-, Rollback-Transaction
-
Cluster listener for leaving nodes
You need to provide a Listener that recognizes when a node leaves the cluster. Has a node left the cluster than your listener must call the recover method on the ClusterCacheJobStore with the node-ID of the node who left. See the Javadoc or InfinispanCacheConnectorSupport.createClusterRecoverListener(..), InfinispanCacheConnectorSupport.getNodeID() and InfinispanClusterManagementListener for a detailed example.
Your newly created CacheConnector may requires some Initialization and needs to be set in the ClusterCacheJobStore. To archive that, you need to inherit from ClusterCacheJobStore and implement the abstract method createCacheConnector() by setting the connector attribute of the Superclass to your CacheConnector.
this.connector = new yourOwnCacheConnector()
To tell Quartz that it should use your JobStore you have to set the org.quartz.jobStore.class= attribute in the quartz.properties file to your JobStore class (the class who inherits from ClusterCacheJobStore). If your CacheConnector needs some additional information, for example a JNDI to your key/value store, you can also set that within the quartz.properties file using:
org.quartz.jobStore.yourAttribute: value
yourAttribute is an Attribute in your JobStore class that needs a public setter because Quartz uses reflection to set them.
See com.exxeta.jobstore.infinispan.InfinispanJobStore as example.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.