/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.timer.ejb; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.List; import java.io.*; import javax.ejb.SessionBean; import javax.ejb.SessionContext; import javax.ejb.TimedObject; import javax.ejb.Timer; import javax.ejb.TimerHandle; import javax.ejb.TimerService; import javax.ejb.EJBException; import javax.ejb.NoSuchObjectLocalException; import javax.management.MBeanServerFactory; import javax.management.MBeanServer; import org.jboss.logging.Logger; import org.jboss.test.timer.interfaces.TimerSLSB; import org.jboss.ejb.txtimer.FixedDelayRetryPolicyMBean; /** * Stateless Session Bean Timer Test * * @ejb:bean name="test/timer/TimerSLSB" * display-name="Timer in Stateless Session Bean" * type="Stateless" * transaction-type="Container" * view-type="remote" * jndi-name="ejb/test/timer/TimerSLSB" * * @ejb:transaction type="Required" * @author Thomas Diesler * @author Scott.Stark@jboss.org * @version $Revision: 81036 $ **/ public class TimerSLSBean implements SessionBean, TimedObject { // ------------------------------------------------------------------------- // Static // ------------------------------------------------------------------------- private static HashMap timeoutCounts = new HashMap(); private static Logger log = Logger.getLogger(TimerSLSBean.class); // ------------------------------------------------------------------------- // Members // ------------------------------------------------------------------------- private SessionContext context; // ------------------------------------------------------------------------- // Methods // ------------------------------------------------------------------------- /** * Start a single timer (if not already set) with the start date plus the period * Uses the string "TimerSLSBean.startSingleTimer" as the timer info data. * * @param pPeriod Time that will elapse between now and the timed event in milliseconds * * @ejb:interface-method view-type="remote" **/ public byte[] startSingleTimer(long pPeriod) { return startSingleTimer(pPeriod,"TimerSLSBean.startSingleTimer"); } /** * Start a single timer (if not already set) with the start date plus the period and specified info. * * @param pPeriod Time that will elapse between now and the timed event in milliseconds * @param info an object to be used as the info for the timer. * * @ejb:interface-method view-type="remote" **/ public byte[] startSingleTimer(long pPeriod, Serializable info) { log.info("TimerSLSBean.startSingleTimer(), try to get a Timer Service from the Session Context"); TimerService ts = context.getTimerService(); long exp = System.currentTimeMillis() + pPeriod; Timer timer = ts.createTimer(new Date(exp), info); log.info("TimerSLSBean.startSingleTimer(), create a timer: "+timer); byte[] handle = getHandle(timer); return handle; } /** * Start a timer (if not already set) with the start date plus the period * and an interval of the given period * Uses the string "TimerSLSBean.startTimer" as the timer info data. * * @param pPeriod Time that will elapse between two events in milliseconds * * @ejb:interface-method view-type="remote" **/ public byte[] startTimer(long pPeriod) { return startTimer(pPeriod, "TimerSLSBean.startTimer"); } /** * Start a timer (if not already set) with the start date plus the period * and an interval of the given period * * @param pPeriod Time that will elapse between two events in milliseconds * @param info an object to be used as the info for the timer. * * @ejb:interface-method view-type="remote" **/ public byte[] startTimer(long pPeriod, Serializable info) { log.info("TimerSLSBean.startTimer(), try to get a Timer Service from the Session Context"); TimerService ts = context.getTimerService(); long exp = System.currentTimeMillis() + pPeriod; Timer timer = ts.createTimer(new Date(exp), pPeriod, info); log.info("TimerSLSBean.startTimer(), create a timer: "+timer); byte[] handle = getHandle(timer); return handle; } /** * @ejb:interface-method view-type="remote" **/ public void stopTimer(byte[] handle) { Timer timer = getTimer(handle); timer.cancel(); log.info("TimerSLSBean.stopTimer(), create a timer: "+timer); synchronized( TimerSLSBean.class ) { Long key = getKey(handle); timeoutCounts.remove(key); } } /** * @ejb:interface-method view-type="remote" **/ public int getTimeoutCount(byte[] handle) { Integer count = null; try { Long key = getKey(handle); count = (Integer) timeoutCounts.get(key); } catch(NoSuchObjectLocalException e) { // Expected if the timer has been stopped } log.info("TimerSLSBean.getTimeoutCount(): " + count); return count != null ? count.intValue() : 0; } /** * @return Date of the next timed event * * @ejb:interface-method view-type="remote" **/ public Date getNextTimeout(byte[] handle) { Timer timer = getTimer(handle); return timer.getNextTimeout(); } /** * @return Time remaining until next timed event in milliseconds * * @ejb:interface-method view-type="remote" **/ public long getTimeRemaining(byte[] handle) { Timer timer = getTimer(handle); return timer.getTimeRemaining(); } /** * @return User object of the timer * * @ejb:interface-method view-type="remote" **/ public Object getInfo(byte[] handle) { Timer timer = getTimer(handle); return timer.getInfo(); } /** * Create the Session Bean * * @ejb:create-method view-type="both" **/ public void ejbCreate() { log.info("TimerSLSBean.ejbCreate()"); } public void ejbTimeout(Timer timer) { Integer count = null; Long key = null; synchronized( TimerSLSBean.class ) { log.debug("ejbTimeout(): Timer State:" + timer); byte[] handle = getHandle(timer); key = getKey(handle); count = (Integer) timeoutCounts.get(key); if( count == null ) count = new Integer(1); else count = new Integer(1 + count.intValue()); timeoutCounts.put(key, count); log.info("ejbTimeout(): count for timer handle " + key + " is " + count); } log.info("ejbTimeout(), timer: " + timer+", key: "+key+", count: "+count); Object info = timer.getInfo(); if(info instanceof Map) { Map mInfo = ((Map)info); Integer failCount = (Integer) mInfo.get(TimerSLSB.INFO_EXEC_FAIL_COUNT); Integer taskTime = (Integer) mInfo.get(TimerSLSB.INFO_TASK_RUNTIME); // If the timer is supposed to fail (testing the retry mechanism) // then we simply rollback the trans. Note this will still increase // the timeoutCounts which is what we want. if(failCount != null && count.compareTo(failCount) <= 0) { log.info("ejbTimeout(): Failing timeout because '" + TimerSLSB.INFO_EXEC_FAIL_COUNT + "' is set to " + failCount + " and count is " + count); context.setRollbackOnly(); return; } // Make method simulate a long running task // This is used to test the case in JBAS-1926 if(taskTime != null) { try { log.info("ejbTimeout(): Simulating long task ("+ taskTime +"ms)"); Thread.sleep(taskTime.intValue()); } catch (InterruptedException e) {} } } } /** * Describes the instance and its content for debugging purpose * * @return Debugging information about the instance and its content **/ public String toString() { return "TimerSLSBean [ " + " ]"; } // ------------------------------------------------------------------------- // Framework Callbacks // ------------------------------------------------------------------------- public void setSessionContext(SessionContext aContext) { context = aContext; } public void ejbActivate() { } public void ejbPassivate() { } public void ejbRemove() { } private Long getKey(byte[] handle) { long key = 0; for(int n = 0; n < handle.length; n ++) key += handle[n]; log.info("HandleKey: "+key); return new Long(key); } private byte[] getHandle(Timer timer) throws EJBException { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(timer.getHandle()); oos.close(); byte[] handle = baos.toByteArray(); return handle; } catch (Exception e) { throw new EJBException("Failed to get timer from handle", e); } } private Timer getTimer(byte[] handle) throws NoSuchObjectLocalException, EJBException { try { ByteArrayInputStream bais = new ByteArrayInputStream(handle); ObjectInputStream ois = new ObjectInputStream(bais); TimerHandle th = null; th = (TimerHandle) ois.readObject(); ois.close(); Timer timer = th.getTimer(); return timer; } catch(NoSuchObjectLocalException e) { throw e; } catch (Exception e) { throw new EJBException("Failed to get timer from handle", e); } } /** * Returns the value from the RetryPolicyMBean. This is used by unit tests to help determine timing * for some of the tests, specifically, those that test the fix for JBAS-1926. */ public long getRetryTimeoutPeriod() { List lServers = MBeanServerFactory.findMBeanServer( null ); MBeanServer lServer = (MBeanServer) lServers.get( 0 ); try { Long val = (Long) lServer.getAttribute(FixedDelayRetryPolicyMBean.OBJECT_NAME, "Delay"); return val.longValue(); } catch (Exception e) { log.error(e); e.printStackTrace(); return -1; } } }