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.test.timer.ejb;
023:
024: import java.util.Date;
025: import java.util.HashMap;
026: import java.util.Map;
027: import java.util.List;
028: import java.io.ByteArrayOutputStream;
029: import java.io.ObjectOutputStream;
030: import java.io.ByteArrayInputStream;
031: import java.io.ObjectInputStream;
032: import java.io.Serializable;
033:
034: import javax.ejb.SessionBean;
035: import javax.ejb.SessionContext;
036: import javax.ejb.TimedObject;
037: import javax.ejb.Timer;
038: import javax.ejb.TimerHandle;
039: import javax.ejb.TimerService;
040: import javax.ejb.EJBException;
041: import javax.ejb.NoSuchObjectLocalException;
042: import javax.management.*;
043:
044: import org.apache.log4j.Logger;
045: import org.jboss.test.timer.interfaces.TimerSLSB;
046: import org.jboss.ejb.txtimer.FixedDelayRetryPolicyMBean;
047:
048: /**
049: * Stateless Session Bean Timer Test
050: *
051: * @ejb:bean name="test/timer/TimerSLSB"
052: * display-name="Timer in Stateless Session Bean"
053: * type="Stateless"
054: * transaction-type="Container"
055: * view-type="remote"
056: * jndi-name="ejb/test/timer/TimerSLSB"
057: *
058: * @ejb:transaction type="Required"
059: * @author Thomas Diesler
060: * @author Scott.Stark@jboss.org
061: * @version $Revision: 57211 $
062: **/
063: public class TimerSLSBean implements SessionBean, TimedObject {
064: // -------------------------------------------------------------------------
065: // Static
066: // -------------------------------------------------------------------------
067: private static HashMap timeoutCounts = new HashMap();
068: private static Logger log = Logger.getLogger(TimerSLSBean.class);
069:
070: // -------------------------------------------------------------------------
071: // Members
072: // -------------------------------------------------------------------------
073: private SessionContext context;
074:
075: // -------------------------------------------------------------------------
076: // Methods
077: // -------------------------------------------------------------------------
078:
079: /**
080: * Start a single timer (if not already set) with the start date plus the period
081: * Uses the string "TimerSLSBean.startSingleTimer" as the timer info data.
082: *
083: * @param pPeriod Time that will elapse between now and the timed event in milliseconds
084: *
085: * @ejb:interface-method view-type="remote"
086: **/
087: public byte[] startSingleTimer(long pPeriod) {
088: return startSingleTimer(pPeriod,
089: "TimerSLSBean.startSingleTimer");
090: }
091:
092: /**
093: * Start a single timer (if not already set) with the start date plus the period and specified info.
094: *
095: * @param pPeriod Time that will elapse between now and the timed event in milliseconds
096: * @param info an object to be used as the info for the timer.
097: *
098: * @ejb:interface-method view-type="remote"
099: **/
100: public byte[] startSingleTimer(long pPeriod, Serializable info) {
101: log
102: .info("TimerSLSBean.startSingleTimer(), try to get a Timer Service from the Session Context");
103: TimerService ts = context.getTimerService();
104: long exp = System.currentTimeMillis() + pPeriod;
105: Timer timer = ts.createTimer(new Date(exp), info);
106: log.info("TimerSLSBean.startSingleTimer(), create a timer: "
107: + timer);
108: byte[] handle = getHandle(timer);
109: return handle;
110: }
111:
112: /**
113: * Start a timer (if not already set) with the start date plus the period
114: * and an interval of the given period
115: * Uses the string "TimerSLSBean.startTimer" as the timer info data.
116: *
117: * @param pPeriod Time that will elapse between two events in milliseconds
118: *
119: * @ejb:interface-method view-type="remote"
120: **/
121: public byte[] startTimer(long pPeriod) {
122: return startTimer(pPeriod, "TimerSLSBean.startTimer");
123: }
124:
125: /**
126: * Start a timer (if not already set) with the start date plus the period
127: * and an interval of the given period
128: *
129: * @param pPeriod Time that will elapse between two events in milliseconds
130: * @param info an object to be used as the info for the timer.
131: *
132: * @ejb:interface-method view-type="remote"
133: **/
134: public byte[] startTimer(long pPeriod, Serializable info) {
135: log
136: .info("TimerSLSBean.startTimer(), try to get a Timer Service from the Session Context");
137: TimerService ts = context.getTimerService();
138: long exp = System.currentTimeMillis() + pPeriod;
139: Timer timer = ts.createTimer(new Date(exp), pPeriod, info);
140: log.info("TimerSLSBean.startTimer(), create a timer: " + timer);
141: byte[] handle = getHandle(timer);
142: return handle;
143: }
144:
145: /**
146: * @ejb:interface-method view-type="remote"
147: **/
148: public void stopTimer(byte[] handle) {
149: Timer timer = getTimer(handle);
150: timer.cancel();
151: log.info("TimerSLSBean.stopTimer(), create a timer: " + timer);
152: synchronized (TimerSLSBean.class) {
153: Long key = getKey(handle);
154: timeoutCounts.remove(key);
155: }
156: }
157:
158: /**
159: * @ejb:interface-method view-type="remote"
160: **/
161: public int getTimeoutCount(byte[] handle) {
162: Integer count = null;
163: try {
164: Long key = getKey(handle);
165: count = (Integer) timeoutCounts.get(key);
166: } catch (NoSuchObjectLocalException e) {
167: // Expected if the timer has been stopped
168: }
169: log.info("TimerSLSBean.getTimeoutCount(): " + count);
170: return count != null ? count.intValue() : 0;
171: }
172:
173: /**
174: * @return Date of the next timed event
175: *
176: * @ejb:interface-method view-type="remote"
177: **/
178: public Date getNextTimeout(byte[] handle) {
179: Timer timer = getTimer(handle);
180: return timer.getNextTimeout();
181: }
182:
183: /**
184: * @return Time remaining until next timed event in milliseconds
185: *
186: * @ejb:interface-method view-type="remote"
187: **/
188: public long getTimeRemaining(byte[] handle) {
189: Timer timer = getTimer(handle);
190: return timer.getTimeRemaining();
191: }
192:
193: /**
194: * @return User object of the timer
195: *
196: * @ejb:interface-method view-type="remote"
197: **/
198: public Object getInfo(byte[] handle) {
199: Timer timer = getTimer(handle);
200: return timer.getInfo();
201: }
202:
203: /**
204: * Create the Session Bean
205: *
206: * @ejb:create-method view-type="both"
207: **/
208: public void ejbCreate() {
209: log.info("TimerSLSBean.ejbCreate()");
210: }
211:
212: public void ejbTimeout(Timer timer) {
213: Integer count = null;
214: Long key = null;
215: synchronized (TimerSLSBean.class) {
216: log.debug("ejbTimeout(): Timer State:" + timer);
217: byte[] handle = getHandle(timer);
218: key = getKey(handle);
219: count = (Integer) timeoutCounts.get(key);
220: if (count == null)
221: count = new Integer(1);
222: else
223: count = new Integer(1 + count.intValue());
224: timeoutCounts.put(key, count);
225: log.info("ejbTimeout(): count for timer handle " + key
226: + " is " + count);
227: }
228:
229: log.info("ejbTimeout(), timer: " + timer + ", key: " + key
230: + ", count: " + count);
231:
232: Object info = timer.getInfo();
233: if (info instanceof Map) {
234: Map mInfo = ((Map) info);
235: Integer failCount = (Integer) mInfo
236: .get(TimerSLSB.INFO_EXEC_FAIL_COUNT);
237: Integer taskTime = (Integer) mInfo
238: .get(TimerSLSB.INFO_TASK_RUNTIME);
239:
240: // If the timer is supposed to fail (testing the retry mechanism)
241: // then we simply rollback the trans. Note this will still increase
242: // the timeoutCounts which is what we want.
243: if (failCount != null && count.compareTo(failCount) <= 0) {
244: log.info("ejbTimeout(): Failing timeout because '"
245: + TimerSLSB.INFO_EXEC_FAIL_COUNT
246: + "' is set to " + failCount + " and count is "
247: + count);
248: context.setRollbackOnly();
249: return;
250: }
251:
252: // Make method simulate a long running task
253: // This is used to test the case in JBAS-1926
254: if (taskTime != null) {
255: try {
256: log.info("ejbTimeout(): Simulating long task ("
257: + taskTime + "ms)");
258: Thread.sleep(taskTime.intValue());
259: } catch (InterruptedException e) {
260: }
261: }
262: }
263: }
264:
265: /**
266: * Describes the instance and its content for debugging purpose
267: *
268: * @return Debugging information about the instance and its content
269: **/
270: public String toString() {
271: return "TimerSLSBean [ " + " ]";
272: }
273:
274: // -------------------------------------------------------------------------
275: // Framework Callbacks
276: // -------------------------------------------------------------------------
277:
278: public void setSessionContext(SessionContext aContext) {
279: context = aContext;
280: }
281:
282: public void ejbActivate() {
283: }
284:
285: public void ejbPassivate() {
286: }
287:
288: public void ejbRemove() {
289: }
290:
291: private Long getKey(byte[] handle) {
292: long key = 0;
293: for (int n = 0; n < handle.length; n++)
294: key += handle[n];
295: log.info("HandleKey: " + key);
296: return new Long(key);
297: }
298:
299: private byte[] getHandle(Timer timer) throws EJBException {
300: try {
301: ByteArrayOutputStream baos = new ByteArrayOutputStream();
302: ObjectOutputStream oos = new ObjectOutputStream(baos);
303: oos.writeObject(timer.getHandle());
304: oos.close();
305: byte[] handle = baos.toByteArray();
306: return handle;
307: } catch (Exception e) {
308: throw new EJBException("Failed to get timer from handle", e);
309: }
310: }
311:
312: private Timer getTimer(byte[] handle)
313: throws NoSuchObjectLocalException, EJBException {
314: try {
315: ByteArrayInputStream bais = new ByteArrayInputStream(handle);
316: ObjectInputStream ois = new ObjectInputStream(bais);
317: TimerHandle th = null;
318: th = (TimerHandle) ois.readObject();
319: ois.close();
320: Timer timer = th.getTimer();
321: return timer;
322: } catch (NoSuchObjectLocalException e) {
323: throw e;
324: } catch (Exception e) {
325: throw new EJBException("Failed to get timer from handle", e);
326: }
327: }
328:
329: /**
330: * Returns the value from the RetryPolicyMBean. This is used by unit tests to help determine timing
331: * for some of the tests, specifically, those that test the fix for JBAS-1926.
332: */
333: public long getRetryTimeoutPeriod() {
334: List lServers = MBeanServerFactory.findMBeanServer(null);
335: MBeanServer lServer = (MBeanServer) lServers.get(0);
336: try {
337: Long val = (Long) lServer.getAttribute(
338: FixedDelayRetryPolicyMBean.OBJECT_NAME, "Delay");
339: return val.longValue();
340: } catch (Exception e) {
341: log.error(e);
342: e.printStackTrace();
343:
344: return -1;
345: }
346: }
347:
348: }
|