001: /**********************************************************************************
002: * $URL$
003: * $Id$
004: **********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005 The Regents of the University of Michigan, Trustees of Indiana University,
007: * Board of Trustees of the Leland Stanford, Jr., University, and The MIT Corporation
008: *
009: * Licensed under the Educational Community License Version 1.0 (the "License");
010: * By obtaining, using and/or copying this Original Work, you agree that you have read,
011: * understand, and will comply with the terms and conditions of the Educational Community License.
012: * You may obtain a copy of the License at:
013: *
014: * http://cvs.sakaiproject.org/licenses/license_1_0.html
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
017: * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
018: * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
019: * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
020: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
021: *
022: **********************************************************************************/package org.sakaiproject.component.app.scheduler;
023:
024: import java.io.InputStream;
025: import java.util.*;
026:
027: import org.apache.commons.dbcp.BasicDataSource;
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.quartz.JobDetail;
031: import org.quartz.Scheduler;
032: import org.quartz.SchedulerException;
033: import org.quartz.SchedulerFactory;
034: import org.quartz.TriggerListener;
035: import org.quartz.impl.StdSchedulerFactory;
036: import org.sakaiproject.api.app.scheduler.SchedulerManager;
037: import org.sakaiproject.api.app.scheduler.JobBeanWrapper;
038: import org.sakaiproject.db.api.SqlService;
039:
040: public class SchedulerManagerImpl implements SchedulerManager {
041:
042: private BasicDataSource dataSource;
043: private String serverId;
044: private Set qrtzJobs;
045: private Map qrtzQualifiedJobs = new TreeMap(); // map for SelectItems
046: private String qrtzPropFile;
047: private Properties qrtzProperties;
048: private TriggerListener globalTriggerListener;
049: private Boolean autoDdl;
050: private Map beanJobs = new Hashtable();
051:
052: private static final String JOB_INTERFACE = "org.quartz.Job";
053:
054: private SchedulerFactory schedFactory;
055: private Scheduler scheduler;
056: private static final Log LOG = LogFactory
057: .getLog(SchedulerManagerImpl.class);
058:
059: public void init() {
060:
061: try {
062:
063: SqlService sqlService = org.sakaiproject.db.cover.SqlService
064: .getInstance();
065:
066: // load quartz properties file
067: InputStream propertiesInputStream = this .getClass()
068: .getResourceAsStream(qrtzPropFile);
069: qrtzProperties = new Properties();
070: qrtzProperties.load(propertiesInputStream);
071:
072: // now replace properties from those loaded in from components.xml
073: // qrtzProperties.setProperty("org.quartz.dataSource.myDS.driver",
074: // dataSource.getDriverClassName());
075: // qrtzProperties.setProperty("org.quartz.dataSource.myDS.URL", dataSource
076: // .getUrl());
077: // qrtzProperties.setProperty("org.quartz.dataSource.myDS.user", dataSource
078: // .getUsername());
079: // qrtzProperties.setProperty("org.quartz.dataSource.myDS.password",
080: // dataSource.getPassword());
081: qrtzProperties.setProperty(
082: "org.quartz.scheduler.instanceId", serverId);
083:
084: // if ("hsqldb".equalsIgnoreCase(sqlService.getVendor())){
085: // qrtzProperties.setProperty("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.HSQLDBDelegate");
086: // }
087: // else if ("mysql".equalsIgnoreCase(sqlService.getVendor())){
088: // qrtzProperties.setProperty("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
089: // }
090: // else if ("oracle".equalsIgnoreCase(sqlService.getVendor())){
091: // qrtzProperties.setProperty("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.oracle.OracleDelegate");
092: // }
093: // else{
094: // LOG.warn("sakai vendor not supported");
095: // }
096:
097: // note: becuase job classes are jarred , it is impossible to iterate
098: // through a directory by calling listFiles on a file object.
099: // Therefore, we need the class list list from spring.
100:
101: // find quartz jobs from specified 'qrtzJobs' and verify they
102: // that these jobs implement the Job interface
103: Iterator qrtzJobsIterator = qrtzJobs.iterator();
104: while (qrtzJobsIterator.hasNext()) {
105: String className = (String) qrtzJobsIterator.next();
106: Class cl = null;
107: try {
108: cl = Class.forName(className);
109: } catch (ClassNotFoundException e) {
110: LOG.warn("Could not locate class: " + className
111: + " on classpath");
112: }
113: if (cl != null) {
114: // check that each class implements the Job interface
115: if (doesImplementJobInterface(cl)) {
116: qrtzQualifiedJobs.put(cl.getName(), cl
117: .getName());
118: } else {
119: LOG
120: .warn("Class: "
121: + className
122: + " does not implement quartz Job interface");
123: }
124: }
125: }
126:
127: // run ddl
128: if (autoDdl.booleanValue()) {
129: try {
130: sqlService.ddl(this .getClass().getClassLoader(),
131: "quartz");
132: } catch (Throwable t) {
133: LOG.warn(this + ".init(): ", t);
134: }
135: }
136:
137: // start scheduler and load jobs
138: schedFactory = new StdSchedulerFactory(qrtzProperties);
139: scheduler = schedFactory.getScheduler();
140:
141: // loop through persisted jobs removing both the job and associated
142: // triggers for jobs where the associated job class is not found
143: String[] arrJobs = scheduler
144: .getJobNames(Scheduler.DEFAULT_GROUP);
145: for (int i = 0; i < arrJobs.length; i++) {
146: try {
147: JobDetail jd = scheduler.getJobDetail(arrJobs[i],
148: Scheduler.DEFAULT_GROUP);
149: } catch (SchedulerException e) {
150: LOG
151: .warn("scheduler cannot load class for persistent job:"
152: + arrJobs[i]);
153: scheduler.deleteJob(arrJobs[i],
154: Scheduler.DEFAULT_GROUP);
155: }
156: }
157:
158: scheduler.addGlobalTriggerListener(globalTriggerListener);
159: scheduler.start();
160: } catch (Exception e) {
161: e.printStackTrace();
162: throw new Error("Scheduler cannot start!");
163: }
164:
165: }
166:
167: private boolean doesImplementJobInterface(Class cl) {
168: Class[] classArr = cl.getInterfaces();
169: for (int i = 0; i < classArr.length; i++) {
170: if (classArr[i].getName().equals(JOB_INTERFACE)) {
171: return true;
172: }
173: }
174: return false;
175: }
176:
177: /**
178: * @see org.sakaiproject.api.app.scheduler.SchedulerManager#destroy()
179: */
180: public void destroy() {
181: try {
182: if (!scheduler.isShutdown()) {
183: scheduler.shutdown();
184: }
185: } catch (Throwable t) {
186: LOG.error("An error occurred while stopping the scheduler",
187: t);
188: }
189: }
190:
191: /**
192: * @return Returns the globalTriggerListener.
193: */
194: public TriggerListener getGlobalTriggerListener() {
195: return globalTriggerListener;
196: }
197:
198: /**
199: * @param globalTriggerListener The globalTriggerListener to set.
200: */
201: public void setGlobalTriggerListener(
202: TriggerListener globalTriggerListener) {
203: this .globalTriggerListener = globalTriggerListener;
204: }
205:
206: /**
207: * @return Returns the serverId.
208: */
209: public String getServerId() {
210: return serverId;
211: }
212:
213: /**
214: * @param serverId The serverId to set.
215: */
216: public void setServerId(String serverId) {
217: this .serverId = serverId;
218: }
219:
220: /**
221: * @return Returns the dataSource.
222: */
223: public BasicDataSource getDataSource() {
224: return dataSource;
225: }
226:
227: /**
228: * @param dataSource The dataSource to set.
229: */
230: public void setDataSource(BasicDataSource dataSource) {
231: this .dataSource = dataSource;
232: }
233:
234: /**
235: * @return Returns the qrtzQualifiedJobs.
236: */
237: public Map getQrtzQualifiedJobs() {
238: return qrtzQualifiedJobs;
239: }
240:
241: /**
242: * @param qrtzQualifiedJobs The qrtzQualifiedJobs to set.
243: */
244: public void setQrtzQualifiedJobs(Map qrtzQualifiedJobs) {
245: this .qrtzQualifiedJobs = qrtzQualifiedJobs;
246: }
247:
248: /**
249: * @return Returns the qrtzJobs.
250: */
251: public Set getQrtzJobs() {
252: return qrtzJobs;
253: }
254:
255: /**
256: * @param qrtzJobs The qrtzJobs to set.
257: */
258: public void setQrtzJobs(Set qrtzJobs) {
259: this .qrtzJobs = qrtzJobs;
260: }
261:
262: /**
263: * @return Returns the qrtzPropFile.
264: */
265: public String getQrtzPropFile() {
266: return qrtzPropFile;
267: }
268:
269: /**
270: * @param qrtzPropFile The qrtzPropFile to set.
271: */
272: public void setQrtzPropFile(String qrtzPropFile) {
273: this .qrtzPropFile = qrtzPropFile;
274: }
275:
276: /**
277: * @return Returns the scheduler.
278: */
279: public Scheduler getScheduler() {
280: return scheduler;
281: }
282:
283: /**
284: * @param scheduler The sched to set.
285: */
286: public void setScheduler(Scheduler scheduler) {
287: this .scheduler = scheduler;
288: }
289:
290: /**
291: * @see org.sakaiproject.api.app.scheduler.SchedulerManager#setAutoDdl(java.lang.Boolean)
292: */
293: public void setAutoDdl(Boolean b) {
294: autoDdl = b;
295: }
296:
297: public Map getBeanJobs() {
298: return beanJobs;
299: }
300:
301: public void registerBeanJob(String jobName, JobBeanWrapper job) {
302: getBeanJobs().put(jobName, job);
303: }
304:
305: public JobBeanWrapper getJobBeanWrapper(String beanWrapperId) {
306: return (JobBeanWrapper) getBeanJobs().get(beanWrapperId);
307: }
308: }
|