001: /**
002: * EasyBeans
003: * Copyright (C) 2007 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: QuartzTimerService.java 1970 2007-10-16 11:49:25Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.component.quartz;
025:
026: import static org.quartz.SimpleTrigger.REPEAT_INDEFINITELY;
027:
028: import java.io.Serializable;
029: import java.util.ArrayList;
030: import java.util.Collection;
031: import java.util.Date;
032:
033: import javax.ejb.EJBException;
034: import javax.ejb.Timer;
035: import javax.ejb.TimerService;
036:
037: import org.ow2.easybeans.api.Factory;
038: import org.quartz.JobDetail;
039: import org.quartz.Scheduler;
040: import org.quartz.SchedulerException;
041: import org.quartz.SimpleTrigger;
042: import org.quartz.Trigger;
043:
044: /**
045: * Implementation of the EJB Timer service that is based on the Quartz
046: * framework.
047: * @author Florent Benoit
048: */
049: public class QuartzTimerService implements TimerService {
050:
051: /**
052: * An EJB Timer service is linked to an EasyBeans factory.
053: */
054: private Factory factory = null;
055:
056: /**
057: * The timer service is also linked to a Quartz scheduler.
058: */
059: private Scheduler scheduler = null;
060:
061: /**
062: * Name of the group for triggers.
063: */
064: private String triggerGroupName = null;
065:
066: /**
067: * Trigger name ID.
068: */
069: private long triggerId = 0;
070:
071: /**
072: * JobDetail id.
073: */
074: private long jobDetailId = 0;
075:
076: /**
077: * Build a new instance of the EJB Timer service for the given factory and
078: * the given scheduler.
079: * @param factory the given factory
080: * @param scheduler the given scheduler
081: */
082: public QuartzTimerService(final Factory factory,
083: final Scheduler scheduler) {
084: this .factory = factory;
085: this .scheduler = scheduler;
086: this .triggerGroupName = factory.getId();
087: }
088:
089: /**
090: * Create a single-action timer that expires after a specified duration.
091: * @param duration The number of milliseconds that must elapse before the
092: * timer expires.
093: * @param info Application information to be delivered along with the timer
094: * expiration notification. This can be null.
095: * @return The newly created Timer.
096: * @throws IllegalArgumentException If duration is negative
097: * @throws IllegalStateException If this method is invoked while the
098: * instance is in a state that does not allow access to this method.
099: * @throws EJBException If this method fails due to a system-level failure.
100: */
101: public Timer createTimer(final long duration,
102: final Serializable info) throws IllegalArgumentException,
103: IllegalStateException, EJBException {
104:
105: // Use other method
106: return createTimer(new Date(System.currentTimeMillis()
107: + duration), info);
108: }
109:
110: /**
111: * Create an interval timer whose first expiration occurs after a specified
112: * duration, and whose subsequent expirations occur after a specified
113: * interval.
114: * @param initialDuration The number of milliseconds that must elapse before
115: * the first timer expiration notification.
116: * @param intervalDuration The number of milliseconds that must elapse
117: * between timer expiration notifications. Expiration notifications
118: * are scheduled relative to the time of the first expiration. If
119: * expiration is delayed(e.g. due to the interleaving of other method
120: * calls on the bean) two or more expiration notifications may occur
121: * in close succession to "catch up".
122: * @param info Application information to be delivered along with the timer
123: * expiration. This can be null.
124: * @return The newly created Timer.
125: * @throws IllegalArgumentException If initialDuration is negative, or
126: * intervalDuration is negative.
127: * @throws IllegalStateException If this method is invoked while the
128: * instance is in a state that does not allow access to this method.
129: * @throws EJBException If this method could not complete due to a
130: * system-level failure.
131: */
132: public Timer createTimer(final long initialDuration,
133: final long intervalDuration, final Serializable info)
134: throws IllegalArgumentException, IllegalStateException,
135: EJBException {
136:
137: // Compute start date for initial expiration
138: Date initialExpiration = new Date(System.currentTimeMillis()
139: + initialDuration);
140:
141: // Use the method using Date parameter
142: return createTimer(initialExpiration, intervalDuration, info);
143: }
144:
145: /**
146: * Create a single-action timer that expires at a given point in time.
147: * @param expiration The point in time at which the timer must expire.
148: * @param info Application information to be delivered along with the timer
149: * expiration notification. This can be null.
150: * @return The newly created Timer.
151: * @throws IllegalArgumentException If expiration is null, or
152: * expiration.getTime() is negative.
153: * @throws IllegalStateException If this method is invoked while the
154: * instance is in a state that does not allow access to this method.
155: * @throws EJBException If this method could not complete due to a
156: * system-level failure.
157: */
158: public Timer createTimer(final Date expiration,
159: final Serializable info) throws IllegalArgumentException,
160: IllegalStateException, EJBException {
161:
162: // Create the trigger that won't repeat
163: Trigger trigger = new SimpleTrigger(getTriggerName(),
164: getTriggerGroupName(), expiration);
165:
166: // Get timer
167: return internalTimer(trigger, info);
168: }
169:
170: /**
171: * Create an interval timer whose first expiration occurs at a given point
172: * in time and whose subsequent expirations occur after a specified
173: * interval.
174: * @param initialExpiration The point in time at which the first timer
175: * expiration must occur.
176: * @param intervalDuration The number of milliseconds that must elapse
177: * between timer expiration notifications. Expiration notifications
178: * are scheduled relative to the time of the first expiration. If
179: * expiration is delayed(e.g. due to the interleaving of other method
180: * calls on the bean) two or more expiration notifications may occur
181: * in close succession to "catch up".
182: * @param info Application information to be delivered along with the timer
183: * expiration. This can be null.
184: * @return The newly created Timer.
185: * @throws IllegalArgumentException If initialExpiration is null, or
186: * initialExpiration.getTime() is negative, or intervalDuration is
187: * negative.
188: * @throws IllegalStateException If this method is invoked while the
189: * instance is in a state that does not allow access to this method.
190: * @throws EJBException If this method could not complete due to a
191: * system-level failure.
192: */
193: public Timer createTimer(final Date initialExpiration,
194: final long intervalDuration, final Serializable info)
195: throws IllegalArgumentException, IllegalStateException,
196: EJBException {
197:
198: // Create the trigger (repeat indefinitely)
199: Trigger trigger = new SimpleTrigger(getTriggerName(),
200: getTriggerGroupName(), initialExpiration, null,
201: REPEAT_INDEFINITELY, intervalDuration);
202:
203: // Get timer
204: return internalTimer(trigger, info);
205: }
206:
207: /**
208: * Create a timer object that is sent to the client. Also, create a new job
209: * and send it to the Quartz Scheduler.
210: * @param trigger the object containing the data for the scheduling.
211: * @param info the optional serializable object given by the developer.
212: * @return a Timer object.
213: */
214: private Timer internalTimer(final Trigger trigger,
215: final Serializable info) {
216:
217: // Add stuff into the data of the job detail
218: EasyBeansJobDetailData beansJobDetailData = new EasyBeansJobDetailData();
219: beansJobDetailData.setInfo(info);
220:
221: // Options for finding the factory again
222: Integer easyBeansServerID = factory.getContainer()
223: .getConfiguration().getEZBServer().getID();
224: beansJobDetailData.setEasyBeansServerID(easyBeansServerID);
225: beansJobDetailData.setContainerId(factory.getContainer()
226: .getId());
227: beansJobDetailData.setFactoryName(factory.getClassName());
228:
229: // Build the Job Detail
230: EasyBeansJobDetail jobDetail = new EasyBeansJobDetail(
231: getNewJobDetailName(), getJobDetailGroupName(),
232: beansJobDetailData);
233:
234: // Build a new timer object
235: Timer timer = new EasyBeansTimer(jobDetail, trigger, scheduler);
236:
237: // Add it as a data
238: beansJobDetailData.setTimer(timer);
239:
240: // Schedule the job
241: try {
242: scheduler.scheduleJob(jobDetail, trigger);
243: } catch (SchedulerException e) {
244: throw new EJBException("Cannot schedule the given job '"
245: + jobDetail + "'.", e);
246: }
247:
248: // ...and return the timer
249: return timer;
250: }
251:
252: /**
253: * Get all the active timers associated with this bean.
254: * @return A collection of javax.ejb.Timer objects.
255: * @throws IllegalStateException If this method is invoked while the
256: * instance is in a state that does not allow access to this method.
257: * @throws EJBException If this method could not complete due to a
258: * system-level failure.
259: */
260: public Collection getTimers() throws IllegalStateException,
261: EJBException {
262: Collection<Timer> timers = new ArrayList<Timer>();
263:
264: // Get the list of job names for this group
265: String[] jobNames = null;
266: try {
267: jobNames = scheduler.getJobNames(getJobDetailGroupName());
268: } catch (SchedulerException e) {
269: throw new EJBException(
270: "Unable to get the job names from the scheduler for the group named '"
271: + getJobDetailGroupName() + "'.", e);
272: }
273:
274: // If there are jobs, get the detail and trigger
275: if (jobNames != null) {
276: // For each job name
277: for (String jobName : jobNames) {
278:
279: // Get detail
280: JobDetail jobDetail = null;
281: try {
282: jobDetail = scheduler.getJobDetail(jobName,
283: getJobDetailGroupName());
284: } catch (SchedulerException e) {
285: throw new EJBException(
286: "Cannot get the jobDetail for the jobName '"
287: + jobName + "'.", e);
288: }
289:
290: // Cast to correct object
291: EasyBeansJobDetail easyBeansJobDetail = null;
292: if (jobDetail instanceof EasyBeansJobDetail) {
293: easyBeansJobDetail = (EasyBeansJobDetail) jobDetail;
294: } else {
295: throw new EJBException(
296: "JobDetail found for the job named '"
297: + jobName
298: + "' is not an EasyBeansJobDetail object");
299: }
300:
301: // Get triggers
302: Trigger[] triggers = null;
303: try {
304: triggers = scheduler.getTriggersOfJob(jobName,
305: getJobDetailGroupName());
306: } catch (SchedulerException e) {
307: throw new EJBException(
308: "Cannot get triggers for the job named '"
309: + jobName + "'.", e);
310: }
311:
312: // Should be only once trigger per job
313: if (triggers == null || triggers.length > 1) {
314: throw new EJBException(
315: "Invalid numbers of triggers found for the job named '"
316: + jobName + "'.");
317: }
318:
319: // Build a timer object and return it
320: timers.add(new EasyBeansTimer(easyBeansJobDetail,
321: triggers[0], scheduler));
322: }
323: }
324:
325: // Return the list of the timers for this timer service.
326: return timers;
327: }
328:
329: /**
330: * Get an unique identifier for a Trigger name.
331: * @return a new trigger name
332: */
333: private synchronized String getTriggerName() {
334: return "triggerTimer" + (triggerId++);
335: }
336:
337: /**
338: * Get an unique identifier for a JobDetail name.
339: * @return a new job detail name
340: */
341: private synchronized String getNewJobDetailName() {
342: return "jobDetail" + (jobDetailId++);
343: }
344:
345: /**
346: * Get the group name for each Trigger.
347: * @return the group name
348: */
349: private String getTriggerGroupName() {
350: return triggerGroupName;
351: }
352:
353: /**
354: * Get the group name for each job detail.
355: * @return the group name
356: */
357: private String getJobDetailGroupName() {
358: return "jobDetailGroup" + factory.getClassName();
359: }
360:
361: /**
362: * Gets the Scheduler.
363: * @return the scheduler.
364: */
365: public Scheduler getScheduler() {
366: return scheduler;
367: }
368: }
|