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: * FactoryBean that retrieves a CommonJ {@link commonj.timers.TimerManager}
036: * 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: for (int i = 0; i < this .scheduledTimerListeners.length; i++) {
150: ScheduledTimerListener scheduledTask = this .scheduledTimerListeners[i];
151: Timer timer = null;
152: if (scheduledTask.isOneTimeTask()) {
153: timer = this .timerManager.schedule(scheduledTask
154: .getTimerListener(), scheduledTask.getDelay());
155: } else {
156: if (scheduledTask.isFixedRate()) {
157: timer = this .timerManager.scheduleAtFixedRate(
158: scheduledTask.getTimerListener(),
159: scheduledTask.getDelay(), scheduledTask
160: .getPeriod());
161: } else {
162: timer = this .timerManager.schedule(scheduledTask
163: .getTimerListener(), scheduledTask
164: .getDelay(), scheduledTask.getPeriod());
165: }
166: }
167: this .timers.add(timer);
168: }
169: }
170:
171: //---------------------------------------------------------------------
172: // Implementation of FactoryBean interface
173: //---------------------------------------------------------------------
174:
175: public Object getObject() {
176: return this .timerManager;
177: }
178:
179: public Class getObjectType() {
180: return (this .timerManager != null ? this .timerManager
181: .getClass() : TimerManager.class);
182: }
183:
184: public boolean isSingleton() {
185: return true;
186: }
187:
188: //---------------------------------------------------------------------
189: // Implementation of Lifecycle interface
190: //---------------------------------------------------------------------
191:
192: /**
193: * Resumes the underlying TimerManager (if not shared).
194: * @see commonj.timers.TimerManager#resume()
195: */
196: public void start() {
197: if (!this .shared) {
198: this .timerManager.resume();
199: }
200: }
201:
202: /**
203: * Suspends the underlying TimerManager (if not shared).
204: * @see commonj.timers.TimerManager#suspend()
205: */
206: public void stop() {
207: if (!this .shared) {
208: this .timerManager.suspend();
209: }
210: }
211:
212: /**
213: * Considers the underlying TimerManager as running if it is
214: * neither suspending nor stopping.
215: * @see commonj.timers.TimerManager#isSuspending()
216: * @see commonj.timers.TimerManager#isStopping()
217: */
218: public boolean isRunning() {
219: return (!this .timerManager.isSuspending() && !this .timerManager
220: .isStopping());
221: }
222:
223: //---------------------------------------------------------------------
224: // Implementation of DisposableBean interface
225: //---------------------------------------------------------------------
226:
227: /**
228: * Cancels all statically registered Timers on shutdown,
229: * and stops the underlying TimerManager (if not shared).
230: * @see commonj.timers.Timer#cancel()
231: * @see commonj.timers.TimerManager#stop()
232: */
233: public void destroy() {
234: // Cancel all registered timers.
235: for (Iterator it = this .timers.iterator(); it.hasNext();) {
236: Timer timer = (Timer) it.next();
237: try {
238: timer.cancel();
239: } catch (Throwable ex) {
240: logger.warn("Could not cancel CommonJ Timer", ex);
241: }
242: }
243: this .timers.clear();
244:
245: // Stop the entire TimerManager, if necessary.
246: if (!this .shared) {
247: // May return early, but at least we already cancelled all known Timers.
248: this.timerManager.stop();
249: }
250: }
251:
252: }
|