001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999 Bull S.A.
004: * Contact: jonas-team@objectweb.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: JTimerService.java 8418 2006-06-02 11:30:33Z durieuxp $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas_ejb.container;
025:
026: import java.io.File;
027: import java.io.FileNotFoundException;
028: import java.io.FileOutputStream;
029: import java.io.IOException;
030: import java.io.ObjectOutputStream;
031: import java.io.Serializable;
032: import java.util.ArrayList;
033: import java.util.Collection;
034: import java.util.Date;
035: import java.util.Iterator;
036:
037: import javax.ejb.EJBException;
038: import javax.ejb.Timer;
039: import javax.ejb.TimerHandle;
040: import javax.ejb.TimerService;
041: import javax.ejb.TransactionRolledbackLocalException;
042: import javax.transaction.TransactionManager;
043:
044: import org.objectweb.jonas.jtm.TransactionService;
045: import org.objectweb.jonas.service.ServiceException;
046: import org.objectweb.jonas.service.ServiceManager;
047: import org.objectweb.jonas_timer.TraceTimer;
048: import org.objectweb.util.monolog.api.BasicLevel;
049:
050: /**
051: * JOnAS Implementation of the TimerService interface (from EJB 2.1) One such
052: * object is created the first time a bean calls getTimerService. Basically
053: * manages the list of the Timers for that bean.
054: * @author Philippe Durieux
055: */
056: public class JTimerService implements TimerService {
057:
058: private ArrayList mytimers = new ArrayList();
059:
060: private JFactory bf = null;
061:
062: private JEntitySwitch es = null;
063:
064: private TransactionManager tm;
065:
066: private int timerCount = 0;
067:
068: private static final long MAX_DURATION = 5000000000L;
069:
070: /**
071: * constructor used for MDB or Session beans
072: */
073: public JTimerService(JFactory bf) {
074: this .bf = bf;
075: // Get the transaction service
076: try {
077: TransactionService ts = (TransactionService) ServiceManager
078: .getInstance().getTransactionService();
079: tm = ts.getTransactionManager();
080: } catch (Exception e) {
081: throw new EJBException(
082: "Error when starting the Timer service ", e);
083: }
084:
085: }
086:
087: /**
088: * constructor used for Entity beans
089: */
090: public JTimerService(JEntitySwitch es) {
091: this .es = es;
092: // Get the transaction service
093: try {
094: TransactionService ts = (TransactionService) ServiceManager
095: .getInstance().getTransactionService();
096: tm = ts.getTransactionManager();
097: } catch (Exception e) {
098: throw new EJBException(
099: "Error when starting the Timer service ", e);
100: }
101: bf = es.getBeanFactory();
102: }
103:
104: /**
105: * @return the Transaction Manager
106: */
107: public TransactionManager getTransactionManager() {
108: return tm;
109: }
110:
111: /**
112: * Notify the timer to the listener
113: * @param timer The Timer object that will be notified
114: */
115: public void notify(Timer timer) {
116: if (es != null) {
117: // Entity Bean
118: es.notifyTimeout(timer);
119: } else {
120: if (bf instanceof JMdbFactory) {
121: ((JMdbFactory) bf).notifyTimeout(timer);
122: } else if (bf instanceof JMdbEndpointFactory) {
123: ((JMdbEndpointFactory) bf).notifyTimeout(timer);
124: } else if (bf instanceof JStatelessFactory) {
125: ((JStatelessFactory) bf).notifyTimeout(timer);
126: } else {
127: TraceEjb.logger.log(BasicLevel.ERROR,
128: "Cannot notify this type of bean: " + bf);
129: Thread.dumpStack();
130: }
131: }
132: }
133:
134: /**
135: * Remove the Timer
136: * @param timer The Timer object that will be removed
137: */
138: public void remove(Timer timer) {
139: synchronized (this ) {
140: int index = mytimers.lastIndexOf(timer);
141: if (index == -1) {
142: TraceTimer.logger.log(BasicLevel.WARN,
143: "try to remove unexisting timer");
144: } else {
145: mytimers.remove(index);
146: }
147: }
148: }
149:
150: /**
151: * cancel all timers (when entity bean is removed)
152: */
153: public void cancelAllTimers() {
154: synchronized (this ) {
155: es = null;
156: for (Iterator i = mytimers.iterator(); i.hasNext();) {
157: JTimer timer = (JTimer) i.next();
158: timer.stopTimer();
159: }
160: mytimers.clear();
161: }
162: }
163:
164: /**
165: * get a Timer from the list
166: */
167: public Timer getTimerByTime(long initialDuration,
168: long intervalDuration, Serializable info) {
169: Timer dummy = new JTimer(this , initialDuration,
170: intervalDuration, info);
171: synchronized (this ) {
172: for (Iterator i = mytimers.iterator(); i.hasNext();) {
173: JTimer timer = (JTimer) i.next();
174: if (timer.sameas(dummy)) {
175: return timer;
176: }
177: }
178: }
179: return null;
180: }
181:
182: // ------------------------------------------------------------------------------------
183: // TimerService implementation
184: // ------------------------------------------------------------------------------------
185:
186: /**
187: * Create an interval timer whose first expiration occurs at a given point
188: * in time and whose subsequent expirations occur after a specified
189: * interval.
190: * @param initialExpiration The point in time at which the first timer
191: * expiration must occur.
192: * @param intervalDuration The number of milliseconds that must elapse
193: * between timer expiration notifications.
194: * @param info Application information to be delivered along with the timer
195: * expiration. This can be null.
196: * @return the newly created Timer.
197: * @throws IllegalArgumentException initialExpiration = 0, or
198: * intervalDuration < 0 or initialExpiration.getTime() < 0.
199: * @throws IllegalStateException the instance is in a state that does not
200: * allow access to this method.
201: * @throws EJBException If this method could not complete due to a
202: * system-level failure.
203: */
204: public Timer createTimer(Date initialExpiration,
205: long intervalDuration, Serializable info)
206: throws IllegalArgumentException, IllegalStateException,
207: EJBException {
208: if (initialExpiration == null) {
209: throw new IllegalArgumentException(
210: "expiration date is null");
211: }
212: // avoids negative argument here (add a few milliseconds)
213: long initialDuration = initialExpiration.getTime()
214: - System.currentTimeMillis() + 20;
215: return createTimer(initialDuration, intervalDuration, info);
216: }
217:
218: /**
219: * Create a single-action timer that expires at a given point in time.
220: * @param expiration The point in time at which the timer expiration must
221: * occur.
222: * @param info Application information to be delivered along with the timer
223: * expiration. This can be null.
224: * @return the newly created Timer.
225: * @throws IllegalArgumentException expiration = 0, or expiration.getTime() <
226: * 0.
227: * @throws IllegalStateException the instance is in a state that does not
228: * allow access to this method.
229: * @throws EJBException If this method could not complete due to a
230: * system-level failure.
231: */
232: public Timer createTimer(Date expiration, Serializable info)
233: throws IllegalArgumentException, IllegalStateException,
234: EJBException {
235: return createTimer(expiration, 0, info);
236: }
237:
238: /**
239: * Create an interval timer whose first expiration occurs after a specified
240: * duration, and whose subsequent expirations occur after a specified
241: * interval.
242: * @param initialDuration The number of milliseconds that must elapse before
243: * the first timer expiration notification.
244: * @param intervalDuration The number of milliseconds that must elapse
245: * between timer expiration notifications.
246: * @param info Application information to be delivered along with the timer
247: * expiration. This can be null.
248: * @return the newly created Timer.
249: * @throws IllegalArgumentException initialExpiration = 0, or
250: * intervalDuration < 0.
251: * @throws IllegalStateException the instance is in a state that does not
252: * allow access to this method.
253: * @throws EJBException If this method could not complete due to a
254: * system-level failure.
255: */
256: public Timer createTimer(long initialDuration,
257: long intervalDuration, Serializable info)
258: throws IllegalArgumentException, IllegalStateException,
259: EJBException {
260:
261: // Check that we are not called from entity ejbCreate
262: if (es != null) {
263: es.getPrimaryKey(); // can throw IllegalStateException
264: }
265:
266: // Check duration positive
267: if (initialDuration < 0 || intervalDuration < 0) {
268: throw new IllegalArgumentException("duration is negative");
269: }
270: // Check duration not too long
271: if (initialDuration > MAX_DURATION) {
272: TraceTimer.logger.log(BasicLevel.DEBUG,
273: "Too high duration: " + initialDuration);
274: }
275: if (intervalDuration > MAX_DURATION) {
276: TraceTimer.logger.log(BasicLevel.DEBUG,
277: "Too high duration: " + intervalDuration);
278: }
279:
280: JTimer timer = new JTimer(this , initialDuration,
281: intervalDuration, info);
282: File pdir = bf.getPassivationDir();
283:
284: // Allocate the Timer Id. Must not reallocate an Id already used.
285: int tid;
286: File timerfile = null;
287: synchronized (this ) {
288: while (true) {
289: tid = timerCount++;
290: timerfile = new File(pdir, String.valueOf(tid) + ".tim");
291: if (!timerfile.exists()) {
292: break;
293: }
294: }
295: }
296:
297: if (mytimers.add(timer)) {
298: timer.startTimer();
299: } else {
300: TraceTimer.logger.log(BasicLevel.WARN,
301: "create a timer already known");
302: }
303:
304: // Build a TimerHandle and serialized it on storage in case of server restart.
305: TimerHandle th = timer.getHandle();
306: ObjectOutputStream oos = null;
307: FileOutputStream fos = null;
308: try {
309: TraceTimer.logger.log(BasicLevel.DEBUG,
310: "Writing Timer on disk");
311: fos = new FileOutputStream(timerfile);
312: oos = new ObjectOutputStream(fos);
313: oos.writeObject(th);
314: timer.setFile(timerfile);
315: } catch (IOException e) {
316: TraceTimer.logger.log(BasicLevel.ERROR,
317: "Cannot write Timer on storage");
318: } finally {
319: try {
320: if (fos != null) {
321: fos.close();
322: }
323: if (oos != null) {
324: oos.close();
325: }
326: } catch (Exception e) {
327: TraceTimer.logger.log(BasicLevel.WARN,
328: "Cannot close OutputStream:" + e);
329: }
330: }
331: return timer;
332: }
333:
334: /**
335: * Create a single-action timer that expires after a specified duration.
336: * @param duration The number of milliseconds that must elapse before the
337: * timer expires.
338: * @param info Application information to be delivered along with the timer
339: * expiration. This can be null.
340: * @return the newly created Timer.
341: * @throws IllegalArgumentException initialExpiration = 0, or
342: * intervalDuration < 0.
343: * @throws IllegalStateException the instance is in a state that does not
344: * allow access to this method.
345: * @throws EJBException If this method could not complete due to a
346: * system-level failure.
347: */
348: public Timer createTimer(long duration, Serializable info)
349: throws IllegalArgumentException, IllegalStateException,
350: EJBException {
351: return createTimer(duration, 0, info);
352: }
353:
354: /**
355: * Get all the active timers associated with this bean.
356: * @return A collection of javax.ejb.Timer objects.
357: * @throws IllegalStateException the instance is in a state that does not
358: * allow access to this method.
359: * @throws EJBException If this method could not complete due to a
360: * system-level failure.
361: */
362: public Collection getTimers() throws IllegalStateException,
363: EJBException {
364: // Check that we are not called from entity ejbCreate
365: if (es != null) {
366: es.getPrimaryKey(); // can throw IllegalStateException
367: }
368: ArrayList ret = new ArrayList();
369: for (Iterator i = mytimers.iterator(); i.hasNext();) {
370: JTimer t = (JTimer) i.next();
371: if (!t.isCancelled()) {
372: ret.add(t);
373: }
374: }
375: return ret;
376: }
377:
378: /**
379: * @return the EjbName used to retrieve the bean factory
380: */
381: public String getEjbName() {
382: if (bf != null) {
383: return bf.getEJBName();
384: }
385: if (es != null) {
386: return es.getBeanFactory().getEJBName();
387: }
388: return null;
389: }
390:
391: /**
392: * @return the encoded PK for entity bean timers
393: */
394: public Serializable getPK() {
395: if (es != null) {
396: JEntityFactory ef = (JEntityFactory) es.getBeanFactory();
397: Serializable pks = (Serializable) es.getPrimaryKey();
398: Serializable pk = (Serializable) ef.encodePK(pks);
399: if (TraceEjb.isDebugIc()) {
400: TraceEjb.interp.log(BasicLevel.DEBUG, "pk = " + pks);
401: TraceEjb.interp.log(BasicLevel.DEBUG, "encoded pk = "
402: + pk);
403: }
404: return pk;
405: } else {
406: return null;
407: }
408: }
409:
410: /**
411: * @return the Container File Name
412: */
413: public String getContainer() {
414: JContainer cont = bf.getContainer();
415: return cont.getExternalFileName();
416: }
417: }
|