001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.scheduling.commonj;
018:
019: import java.util.Iterator;
020: import java.util.LinkedList;
021: import java.util.List;
022:
023: import javax.naming.NamingException;
024:
025: import commonj.timers.Timer;
026: import commonj.timers.TimerManager;
027:
028: import org.springframework.beans.factory.DisposableBean;
029: import org.springframework.beans.factory.FactoryBean;
030: import org.springframework.beans.factory.InitializingBean;
031: import org.springframework.context.Lifecycle;
032: import org.springframework.jndi.JndiLocatorSupport;
033:
034: /**
035: * {@link org.springframework.beans.factory.FactoryBean} that retrieves a
036: * CommonJ {@link commonj.timers.TimerManager} and exposes it for bean references.
037: *
038: * <p><b>This is the central convenience class for setting up a
039: * CommonJ TimerManager in a Spring context.</b>
040: *
041: * <p>Allows for registration of ScheduledTimerListeners. This is the main
042: * purpose of this class; the TimerManager itself could also be fetched
043: * from JNDI via {@link org.springframework.jndi.JndiObjectFactoryBean}.
044: * In scenarios that just require static registration of tasks at startup,
045: * there is no need to access the TimerManager itself in application code.
046: *
047: * <p>Note that the TimerManager uses a TimerListener instance that is
048: * shared between repeated executions, in contrast to Quartz which
049: * instantiates a new Job for each execution.
050: *
051: * @author Juergen Hoeller
052: * @since 2.0
053: * @see ScheduledTimerListener
054: * @see commonj.timers.TimerManager
055: * @see commonj.timers.TimerListener
056: */
057: public class TimerManagerFactoryBean extends JndiLocatorSupport
058: implements FactoryBean, InitializingBean, DisposableBean,
059: Lifecycle {
060:
061: private TimerManager timerManager;
062:
063: private String timerManagerName;
064:
065: private boolean shared = false;
066:
067: private ScheduledTimerListener[] scheduledTimerListeners;
068:
069: private final List timers = new LinkedList();
070:
071: /**
072: * Specify the CommonJ TimerManager to delegate to.
073: * <p>Note that the given TimerManager's lifecycle will be managed
074: * by this FactoryBean.
075: * <p>Alternatively (and typically), you can specify the JNDI name
076: * of the target TimerManager.
077: * @see #setTimerManagerName
078: */
079: public void setTimerManager(TimerManager timerManager) {
080: this .timerManager = timerManager;
081: }
082:
083: /**
084: * Set the JNDI name of the CommonJ TimerManager.
085: * <p>This can either be a fully qualified JNDI name, or the JNDI name relative
086: * to the current environment naming context if "resourceRef" is set to "true".
087: * @see #setTimerManager
088: * @see #setResourceRef
089: */
090: public void setTimerManagerName(String timerManagerName) {
091: this .timerManagerName = timerManagerName;
092: }
093:
094: /**
095: * Specify whether the TimerManager obtained by this FactoryBean
096: * is a shared instance ("true") or an independent instance ("false").
097: * The lifecycle of the former is supposed to be managed by the application
098: * server, while the lifecycle of the latter is up to the application.
099: * <p>Default is "false", i.e. managing an independent TimerManager instance.
100: * This is what the CommonJ specification suggests that application servers
101: * are supposed to offer via JNDI lookups, typically declared as a
102: * <code>resource-ref</code> of type <code>commonj.timers.TimerManager</code>
103: * in <code>web.xml<code>, with <code>res-sharing-scope</code> set to 'Unshareable'.
104: * <p>Switch this flag to "true" if you are obtaining a shared TimerManager,
105: * typically through specifying the JNDI location of a TimerManager that
106: * has been explicitly declared as 'Shareable'. Note that WebLogic's
107: * cluster-aware Job Scheduler is a shared TimerManager too.
108: * <p>The sole difference between this FactoryBean being in shared or
109: * non-shared mode is that it will only attempt to suspend / resume / stop
110: * the underlying TimerManager in case of an independent (non-shared) instance.
111: * This only affects the {@link org.springframework.context.Lifecycle} support
112: * as well as application context shutdown.
113: * @see #stop()
114: * @see #start()
115: * @see #destroy()
116: * @see commonj.timers.TimerManager
117: */
118: public void setShared(boolean shared) {
119: this .shared = shared;
120: }
121:
122: /**
123: * Register a list of ScheduledTimerListener objects with the TimerManager
124: * that this FactoryBean creates. Depending on each ScheduledTimerListener's settings,
125: * it will be registered via one of TimerManager's schedule methods.
126: * @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long)
127: * @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long, long)
128: * @see commonj.timers.TimerManager#scheduleAtFixedRate(commonj.timers.TimerListener, long, long)
129: */
130: public void setScheduledTimerListeners(
131: ScheduledTimerListener[] scheduledTimerListeners) {
132: this .scheduledTimerListeners = scheduledTimerListeners;
133: }
134:
135: //---------------------------------------------------------------------
136: // Implementation of InitializingBean interface
137: //---------------------------------------------------------------------
138:
139: public void afterPropertiesSet() throws NamingException {
140: if (this .timerManager == null) {
141: if (this .timerManagerName == null) {
142: throw new IllegalArgumentException(
143: "Either 'timerManager' or 'timerManagerName' must be specified");
144: }
145: this .timerManager = (TimerManager) lookup(
146: this .timerManagerName, TimerManager.class);
147: }
148:
149: if (this .scheduledTimerListeners != null) {
150: for (int i = 0; i < this .scheduledTimerListeners.length; i++) {
151: ScheduledTimerListener scheduledTask = this .scheduledTimerListeners[i];
152: Timer timer = null;
153: if (scheduledTask.isOneTimeTask()) {
154: timer = this .timerManager.schedule(scheduledTask
155: .getTimerListener(), scheduledTask
156: .getDelay());
157: } else {
158: if (scheduledTask.isFixedRate()) {
159: timer = this .timerManager.scheduleAtFixedRate(
160: scheduledTask.getTimerListener(),
161: scheduledTask.getDelay(), scheduledTask
162: .getPeriod());
163: } else {
164: timer = this .timerManager.schedule(
165: scheduledTask.getTimerListener(),
166: scheduledTask.getDelay(), scheduledTask
167: .getPeriod());
168: }
169: }
170: this .timers.add(timer);
171: }
172: }
173: }
174:
175: //---------------------------------------------------------------------
176: // Implementation of FactoryBean interface
177: //---------------------------------------------------------------------
178:
179: public Object getObject() {
180: return this .timerManager;
181: }
182:
183: public Class getObjectType() {
184: return (this .timerManager != null ? this .timerManager
185: .getClass() : TimerManager.class);
186: }
187:
188: public boolean isSingleton() {
189: return true;
190: }
191:
192: //---------------------------------------------------------------------
193: // Implementation of Lifecycle interface
194: //---------------------------------------------------------------------
195:
196: /**
197: * Resumes the underlying TimerManager (if not shared).
198: * @see commonj.timers.TimerManager#resume()
199: */
200: public void start() {
201: if (!this .shared) {
202: this .timerManager.resume();
203: }
204: }
205:
206: /**
207: * Suspends the underlying TimerManager (if not shared).
208: * @see commonj.timers.TimerManager#suspend()
209: */
210: public void stop() {
211: if (!this .shared) {
212: this .timerManager.suspend();
213: }
214: }
215:
216: /**
217: * Considers the underlying TimerManager as running if it is
218: * neither suspending nor stopping.
219: * @see commonj.timers.TimerManager#isSuspending()
220: * @see commonj.timers.TimerManager#isStopping()
221: */
222: public boolean isRunning() {
223: return (!this .timerManager.isSuspending() && !this .timerManager
224: .isStopping());
225: }
226:
227: //---------------------------------------------------------------------
228: // Implementation of DisposableBean interface
229: //---------------------------------------------------------------------
230:
231: /**
232: * Cancels all statically registered Timers on shutdown,
233: * and stops the underlying TimerManager (if not shared).
234: * @see commonj.timers.Timer#cancel()
235: * @see commonj.timers.TimerManager#stop()
236: */
237: public void destroy() {
238: // Cancel all registered timers.
239: for (Iterator it = this .timers.iterator(); it.hasNext();) {
240: Timer timer = (Timer) it.next();
241: try {
242: timer.cancel();
243: } catch (Throwable ex) {
244: logger.warn("Could not cancel CommonJ Timer", ex);
245: }
246: }
247: this .timers.clear();
248:
249: // Stop the entire TimerManager, if necessary.
250: if (!this .shared) {
251: // May return early, but at least we already cancelled all known Timers.
252: this.timerManager.stop();
253: }
254: }
255:
256: }
|