001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.txtimer;
023:
024: // $Id: EJBTimerServiceImpl.java 57209 2006-09-26 12:21:57Z dimitris@jboss.org $
025:
026: import java.lang.reflect.Constructor;
027: import java.util.Collection;
028: import java.util.Collections;
029: import java.util.HashMap;
030: import java.util.Iterator;
031: import java.util.List;
032: import java.util.Map;
033:
034: import javax.ejb.TimerService;
035: import javax.management.ObjectName;
036: import javax.transaction.TransactionManager;
037:
038: import org.jboss.ejb.Container;
039: import org.jboss.ejb.ContainerMBean;
040: import org.jboss.logging.Logger;
041: import org.jboss.mx.util.MBeanProxyExt;
042: import org.jboss.system.ServiceMBeanSupport;
043: import org.jboss.tm.TransactionManagerFactory;
044: import org.jboss.tm.TransactionManagerLocator;
045:
046: /**
047: * A service that implements this interface provides an Tx aware EJBTimerService.
048: *
049: * @author Thomas.Diesler@jboss.org
050: * @author Dimitris.Andreadis@jboss.org
051: * @version $Revision: 57209 $
052: * @since 07-Apr-2004
053: */
054: public class EJBTimerServiceImpl extends ServiceMBeanSupport implements
055: EJBTimerServiceImplMBean {
056: // Logging support
057: private static Logger log = Logger
058: .getLogger(EJBTimerServiceImpl.class);
059:
060: // Attributes
061:
062: // The object name of the retry policy
063: private ObjectName retryPolicyName;
064: // The object name of the persistence policy
065: private ObjectName persistencePolicyName;
066: // The TimerIdGenerator class name
067: private String timerIdGeneratorClassName;
068: // The TimedObjectInvoker class name
069: private String timedObjectInvokerClassName;
070: // The TransactionManagerFactory
071: private TransactionManagerFactory transactionManagerFactory;
072:
073: // Plug-ins
074:
075: // The tx manager plug-in
076: private TransactionManager transactionManager;
077: // The retry policy plug-in
078: private RetryPolicy retryPolicy;
079: // The persistence policy plug-in
080: private PersistencePolicy persistencePolicy;
081: // The timerId generator plug-in
082: private TimerIdGenerator timerIdGenerator;
083:
084: // Maps the timedObjectId to TimerServiceImpl objects
085: private Map timerServiceMap = Collections
086: .synchronizedMap(new HashMap());
087:
088: // Attributes ----------------------------------------------------
089:
090: /**
091: * Get the object name of the retry policy.
092: *
093: * @jmx.managed-attribute
094: */
095: public ObjectName getRetryPolicy() {
096: return retryPolicyName;
097: }
098:
099: /**
100: * Set the object name of the retry policy.
101: *
102: * @jmx.managed-attribute
103: */
104: public void setRetryPolicy(ObjectName retryPolicyName) {
105: this .retryPolicyName = retryPolicyName;
106: }
107:
108: /**
109: * Get the object name of the persistence policy.
110: *
111: * @jmx.managed-attribute
112: */
113: public ObjectName getPersistencePolicy() {
114: return persistencePolicyName;
115: }
116:
117: /**
118: * Set the object name of the persistence policy.
119: *
120: * @jmx.managed-attribute
121: */
122: public void setPersistencePolicy(ObjectName persistencePolicyName) {
123: this .persistencePolicyName = persistencePolicyName;
124: }
125:
126: /**
127: * Get the TimerIdGenerator class name
128: *
129: * @jmx.managed-attribute
130: */
131: public String getTimerIdGeneratorClassName() {
132: return timerIdGeneratorClassName;
133: }
134:
135: /**
136: * Get the TimerIdGenerator class name
137: *
138: * @jmx.managed-attribute
139: */
140: public void setTimerIdGeneratorClassName(
141: String timerIdGeneratorClassName) {
142: this .timerIdGeneratorClassName = timerIdGeneratorClassName;
143: }
144:
145: /**
146: * Get the TimedObjectInvoker class name
147: *
148: * @jmx.managed-attribute
149: */
150: public String getTimedObjectInvokerClassName() {
151: return timedObjectInvokerClassName;
152: }
153:
154: /**
155: * Set the TimedObjectInvoker class name
156: *
157: * @jmx.managed-attribute
158: */
159: public void setTimedObjectInvokerClassName(
160: String timedObjectInvokerClassName) {
161: this .timedObjectInvokerClassName = timedObjectInvokerClassName;
162: }
163:
164: /**
165: * Set the TransactionManagerFactory
166: */
167: public void setTransactionManagerFactory(
168: TransactionManagerFactory factory) {
169: this .transactionManagerFactory = factory;
170: }
171:
172: // ServiceMBeanSupport Lifecycle ---------------------------------
173:
174: protected void startService() throws Exception {
175: // Setup plugins, fall back to safe defaults
176:
177: // Get the TransactionManager from the factory, fall-back to the locator
178: if (transactionManagerFactory != null)
179: transactionManager = transactionManagerFactory
180: .getTransactionManager();
181: else
182: transactionManager = TransactionManagerLocator
183: .getInstance().locate();
184:
185: // Get a proxy to the retry policy
186: try {
187: retryPolicy = (RetryPolicy) MBeanProxyExt.create(
188: RetryPolicy.class, getRetryPolicy(), server);
189: } catch (Exception e) {
190: log
191: .error(
192: "Cannot obtain the implementation of a RetryPolicy",
193: e);
194: }
195:
196: // Get a proxy to the persistence policy
197: try {
198: persistencePolicy = (PersistencePolicy) MBeanProxyExt
199: .create(PersistencePolicy.class,
200: persistencePolicyName, server);
201: } catch (Exception e) {
202: log
203: .warn("Cannot obtain the implementation of a PersistencePolicy, using NoopPersistencePolicy: "
204: + e.toString());
205: persistencePolicy = new NoopPersistencePolicy();
206: }
207:
208: // Get the timerId generator
209: try {
210: Class timerIdGeneratorClass = getClass().getClassLoader()
211: .loadClass(timerIdGeneratorClassName);
212: timerIdGenerator = (TimerIdGenerator) timerIdGeneratorClass
213: .newInstance();
214: } catch (Exception e) {
215: log
216: .warn("Cannot obtain the implementation of a TimerIdGenerator, using BigIntegerTimerIdGenerator: "
217: + e.toString());
218: timerIdGenerator = new BigIntegerTimerIdGenerator();
219: }
220: }
221:
222: protected void stopService() {
223: // Cleanup plugins
224: transactionManager = null;
225: retryPolicy = null;
226: persistencePolicy = null;
227: timerIdGenerator = null;
228: }
229:
230: // EJBTimerService Operations ------------------------------------
231:
232: /**
233: * Create a TimerService for a given TimedObjectId that lives in a JBoss Container.
234: * The TimedObjectInvoker is constructed from the invokerClassName.
235: *
236: * @param containerId The string identifier for a class of TimedObjects
237: * @param instancePk The rimary key for an instance of a TimedObject, may be null
238: * @param container The Container that is associated with the TimerService
239: * @return the TimerService
240: */
241: public TimerService createTimerService(ObjectName containerId,
242: Object instancePk, Container container) {
243: TimedObjectInvoker invoker = null;
244: try {
245: TimedObjectId timedObjectId = new TimedObjectId(
246: containerId, instancePk);
247: Class invokerClass = getClass().getClassLoader().loadClass(
248: timedObjectInvokerClassName);
249: Constructor constr = invokerClass
250: .getConstructor(new Class[] { TimedObjectId.class,
251: Container.class });
252: invoker = (TimedObjectInvoker) constr
253: .newInstance(new Object[] { timedObjectId,
254: container });
255: } catch (Exception e) {
256: log.error("Cannot create TimedObjectInvoker: "
257: + timedObjectInvokerClassName, e);
258: return null;
259: }
260:
261: return createTimerService(containerId, instancePk, invoker);
262: }
263:
264: /**
265: * Create a TimerService for a given TimedObjectId that is invoked through the given invoker
266: *
267: * @param containerId The string identifier for a class of TimedObjects
268: * @param instancePk The rimary key for an instance of a TimedObject, may be null
269: * @param invoker The TimedObjectInvoker
270: * @return the TimerService
271: */
272: public TimerService createTimerService(ObjectName containerId,
273: Object instancePk, TimedObjectInvoker invoker) {
274: TimedObjectId timedObjectId = new TimedObjectId(containerId,
275: instancePk);
276: TimerServiceImpl timerService = (TimerServiceImpl) timerServiceMap
277: .get(timedObjectId);
278: if (timerService == null) {
279: timerService = new TimerServiceImpl(timedObjectId, invoker,
280: transactionManager, persistencePolicy, retryPolicy,
281: timerIdGenerator);
282: log.debug("createTimerService: " + timerService);
283: timerServiceMap.put(timedObjectId, timerService);
284: }
285: return timerService;
286: }
287:
288: /**
289: * Get the TimerService for a given TimedObjectId
290: *
291: * @param containerId The string identifier for a class of TimedObjects
292: * @param instancePk The rimary key for an instance of a TimedObject, may be null
293: * @return The TimerService, or null if it does not exist
294: */
295: public TimerService getTimerService(ObjectName containerId,
296: Object instancePk) {
297: TimedObjectId timedObjectId = new TimedObjectId(containerId,
298: instancePk);
299: return (TimerServiceImpl) timerServiceMap.get(timedObjectId);
300: }
301:
302: /**
303: * Remove the TimerService for a given containerId/pKey (TimedObjectId),
304: * along with any persisted timer information.
305: *
306: * This should be used for removing the TimerService and Timers
307: * associated with a particular entity bean, when it gets removed.
308: *
309: * @param containerId The string identifier for a class of TimedObjects
310: * @param pKey The primary key for an instance of a TimedObject, may be null
311: */
312: public void removeTimerService(ObjectName containerId,
313: Object instancePk) {
314: TimedObjectId timedObjectId = new TimedObjectId(containerId,
315: instancePk);
316: // remove a single timer service
317: if (timedObjectId.getInstancePk() != null) {
318: TimerServiceImpl timerService = (TimerServiceImpl) getTimerService(
319: containerId, instancePk);
320: if (timerService != null) {
321: log.debug("removeTimerService: " + timerService);
322: // don't keep persistent state about the timer
323: // this is really an entity->remove()
324: timerService.shutdown(false);
325: timerServiceMap.remove(timedObjectId);
326: }
327: } else {
328: // assume we don't want to keep timer state when the container
329: // gets undeployed, this is the legacy behaviour
330: removeTimerService(containerId, false);
331: }
332: }
333:
334: /**
335: * Remove the TimerService for a given containerId.
336: *
337: * This should be used to remove the timer service and timers for
338: * any type of container (session, entity, message) at the time of
339: * undeployment.
340: *
341: * @param containerId The string identifier for a class of TimedObjects
342: * @param keepState Flag indicating whether timer persistent state should be kept or removed
343: */
344: public void removeTimerService(ObjectName containerId,
345: boolean keepState) throws IllegalStateException {
346: // remove all timers with the given containerId
347: Iterator it = timerServiceMap.entrySet().iterator();
348: while (it.hasNext()) {
349: Map.Entry entry = (Map.Entry) it.next();
350: TimedObjectId key = (TimedObjectId) entry.getKey();
351: TimerServiceImpl timerService = (TimerServiceImpl) entry
352: .getValue();
353: if (containerId.equals(key.getContainerId())) {
354: log.debug("removeTimerService: " + timerService);
355: timerService.shutdown(keepState);
356: it.remove();
357: }
358: }
359: }
360:
361: /**
362: * Remove the TimerService for a given containerId/pKey (TimedObjectId).
363: *
364: * @param containerId The string identifier for a class of TimedObjects
365: * @param pKey The primary key for an instance of a TimedObject, may be null
366: * @param keepState Flag indicating whether timer persistent state should be kept or removed
367: */
368: public void removeTimerService(ObjectName containerId,
369: Object instancePk, boolean keepState)
370: throws IllegalStateException {
371: // remove a single timer service
372: TimedObjectId timedObjectId = new TimedObjectId(containerId,
373: instancePk);
374: if (timedObjectId.getInstancePk() != null) {
375: TimerServiceImpl timerService = (TimerServiceImpl) getTimerService(
376: containerId, instancePk);
377: if (timerService != null) {
378: log.debug("removeTimerService: " + timerService);
379: timerService.shutdown(false);
380: timerServiceMap.remove(timedObjectId);
381: }
382: }
383: // remove all timers with the given containerId
384: else {
385: Iterator it = timerServiceMap.entrySet().iterator();
386: while (it.hasNext()) {
387: Map.Entry entry = (Map.Entry) it.next();
388: TimedObjectId key = (TimedObjectId) entry.getKey();
389: TimerServiceImpl timerService = (TimerServiceImpl) entry
390: .getValue();
391: if (containerId.equals(key.getContainerId())) {
392: log.debug("removeTimerService: " + timerService);
393: timerService.shutdown(keepState);
394: it.remove();
395: }
396: }
397: }
398: }
399:
400: /**
401: * Restore the persisted timers for a given ejb container
402: *
403: * @param containerId The ejb container id
404: * @param loader The classloader to use for loading the timers
405: */
406: public void restoreTimers(ObjectName containerId, ClassLoader loader)
407: throws IllegalStateException {
408: // find out all the persisted handles, for the specified container
409: List handles = persistencePolicy.listTimerHandles(containerId,
410: loader);
411:
412: if (handles.isEmpty() == false) {
413: // first remove the persisted handles from the db
414: for (Iterator i = handles.iterator(); i.hasNext();) {
415: TimerHandleImpl handle = (TimerHandleImpl) i.next();
416: persistencePolicy.deleteTimer(handle.getTimerId(),
417: handle.getTimedObjectId());
418: }
419:
420: // make a second pass to re-create the timers; use the container
421: // itself to retrieve the correct TimerService/ for each handle,
422: // then use the standard ejb timer API to recreate the timer
423: for (Iterator i = handles.iterator(); i.hasNext();) {
424: TimerHandleImpl handle = (TimerHandleImpl) i.next();
425: try {
426: TimedObjectId targetId = handle.getTimedObjectId();
427: ContainerMBean container = (ContainerMBean) MBeanProxyExt
428: .create(ContainerMBean.class, containerId,
429: server);
430: TimerService timerService = container
431: .getTimerService(targetId.getInstancePk());
432: timerService.createTimer(handle.getFirstTime(),
433: handle.getPeriode(), handle.getInfo());
434: } catch (Exception e) {
435: log.warn("Unable to restore timer record: "
436: + handle);
437: }
438: }
439: }
440: }
441:
442: // EJBTimerServiceImplMbean operations ---------------------------
443:
444: /**
445: * List the timers registered with all TimerService objects
446: *
447: * @jmx.managed-operation
448: */
449: public String listTimers() {
450: StringBuffer retBuffer = new StringBuffer();
451: Iterator it = timerServiceMap.entrySet().iterator();
452: while (it.hasNext()) {
453: Map.Entry entry = (Map.Entry) it.next();
454: TimedObjectId timedObjectId = (TimedObjectId) entry
455: .getKey();
456: retBuffer.append(timedObjectId + "\n");
457:
458: TimerServiceImpl timerService = (TimerServiceImpl) entry
459: .getValue();
460: Collection col = timerService.getAllTimers();
461: for (Iterator iterator = col.iterator(); iterator.hasNext();) {
462: TimerImpl timer = (TimerImpl) iterator.next();
463: TimerHandleImpl handle = new TimerHandleImpl(timer);
464: retBuffer.append(" handle: " + handle + "\n");
465: retBuffer.append(" " + timer + "\n");
466: }
467: }
468: return retBuffer.toString();
469: }
470:
471: }
|