/*
* JBoss, Home of Professional Open Source.
* Copyright 2009, 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.ejb3.timerservice.deployer;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
import org.jboss.beans.metadata.api.annotations.Install;
import org.jboss.dependency.spi.ControllerState;
import org.jboss.ejb3.EJBContainer;
import org.jboss.ejb3.timeout.spi.TimeoutMethodCallbackRequirements;
import org.jboss.ejb3.util.Service;
import org.jboss.logging.Logger;
import org.jboss.metadata.ejb.jboss.JBossEnterpriseBeanMetaData;
import org.jboss.metadata.ejb.jboss.JBossMessageDrivenBean31MetaData;
import org.jboss.metadata.ejb.jboss.JBossSessionBean31MetaData;
import org.jboss.metadata.ejb.spec.MethodParametersMetaData;
import org.jboss.metadata.ejb.spec.NamedMethodMetaData;
import org.jboss.metadata.ejb.spec.TimerMetaData;
/**
* Responsible for creating any necessary auto timers for an EJB
*
* <p>
* The {@link AutoTimerInitializer} "listens" for {@link EJBContainer}s which
* reach {@link ControllerState#START} state. The {@link AutoTimerInitializer}
* then checks the EJB metadata associated with such containers and decides whether auto-timers
* have to be created for that container. For any such relevant container, this {@link AutoTimerInitializer}
* then creates the auto-timer(s).
* </p>
*
* @author Jaikiran Pai
* @version $Revision: $
*/
public class AutoTimerInitializer
{
/**
* Logger
*/
private static Logger logger = Logger.getLogger(AutoTimerInitializer.class);
/** timeout method requirements checker */
private static TimeoutMethodCallbackRequirements timeoutMethodCallbackRequirements = Service
.loadService(TimeoutMethodCallbackRequirements.class);
/**
* Creates auto-timer(s) for the passed {@link EJBContainer} if the corresponding EJB has auto-timers
* configured
*
* @param container The EJB container
*/
@Install (dependentState = "Start") // we work with containers in "Start" state because that's when the container
// is available for invocations (for timeout methods).
public void initializeAutoTimers(EJBContainer container)
{
JBossEnterpriseBeanMetaData enterpriseBeanMetaData = container.getXml();
// Auto timers are only since EJB3.1
// TODO: This check will fail when EJB3.2 or any newer versions are introduced.
// A better check would be to look for any version greater than EJB3.1
if (enterpriseBeanMetaData.getJBossMetaData().isEJB31() == false)
{
return;
}
List<TimerMetaData> autoTimersMetaData = null;
// Session bean
if (enterpriseBeanMetaData.isSession() && enterpriseBeanMetaData instanceof JBossSessionBean31MetaData)
{
JBossSessionBean31MetaData sessionBean = (JBossSessionBean31MetaData) enterpriseBeanMetaData;
// Stateful beans don't have timerservice/timers
if (sessionBean.isStateful())
{
return;
}
// Get hold of the auto timer metadata
autoTimersMetaData = sessionBean.getTimers();
}
// MDB
else if (enterpriseBeanMetaData.isMessageDriven()
&& enterpriseBeanMetaData instanceof JBossMessageDrivenBean31MetaData)
{
JBossMessageDrivenBean31MetaData mdb = (JBossMessageDrivenBean31MetaData) enterpriseBeanMetaData;
// get hold of auto timer metadata
autoTimersMetaData = mdb.getTimers();
}
// If there's no auto timers, then there's nothing to do
if (autoTimersMetaData == null)
{
return;
}
// get hold of the timerservice since we need it to create the autotimers
TimerService timerService = container.getTimerService();
if (timerService instanceof org.jboss.ejb3.timerservice.extension.TimerService == false)
{
// can't do anything about this
logger.warn("Cannot create auto timers for EJB: " + enterpriseBeanMetaData.getEjbName()
+ " since the timerservice isn't of type "
+ org.jboss.ejb3.timerservice.extension.TimerService.class.getName());
return;
}
org.jboss.ejb3.timerservice.extension.TimerService ejb31TimerService = (org.jboss.ejb3.timerservice.extension.TimerService) timerService;
// process each auto timer
for (TimerMetaData autoTimerMetaData : autoTimersMetaData)
{
// create a timer config
TimerConfig timerConfig = new TimerConfig();
timerConfig.setPersistent(autoTimerMetaData.isPersistent());
String info = autoTimerMetaData.getInfo();
if (info != null && !info.isEmpty())
{
timerConfig.setInfo(autoTimerMetaData.getInfo());
}
NamedMethodMetaData timeoutMethodMetaData = autoTimerMetaData.getTimeoutMethod();
// get hold of the timeout method for this auto-timer
Method timeoutMethod = this.getTimeoutMethod(timeoutMethodMetaData, container.getBeanClass());
if (timeoutMethod == null)
{
StringBuilder methodStringBuilder = new StringBuilder();
methodStringBuilder.append(timeoutMethodMetaData.getMethodName());
if (timeoutMethodMetaData.getMethodParams() != null)
{
methodStringBuilder.append(Arrays.toString(timeoutMethodMetaData.getMethodParams().toArray()));
}
throw new IllegalStateException("Timeout method: " + methodStringBuilder.toString()
+ " not found for bean class: " + enterpriseBeanMetaData.getEjbClass());
}
// finally create/get the auto timer
ejb31TimerService.getAutoTimer(autoTimerMetaData.getScheduleExpression(), timerConfig, timeoutMethod);
}
}
/**
* Returns the {@link Method}, represented by the {@link NamedMethodMetaData}, from the <code>beanClass</code>
* <p>
* This method looks for private, protected, package and public methods on the <code>beanClass</code>
* and its superclass(es). If no matching method is found, then this method returns null.
* </p>
* @param timeoutMethodMetaData The method metadata
* @param beanClass The class on which the method has to be looked for
* @return
* @throws NullPointerException If either of the passed parameters is null
*/
private Method getTimeoutMethod(NamedMethodMetaData timeoutMethodMetaData, Class<?> beanClass)
{
String timeoutMethodName = timeoutMethodMetaData.getMethodName();
MethodParametersMetaData timeoutMethodParams = timeoutMethodMetaData.getMethodParams();
Class<?>[] timeoutMethodParamTypes = null;
if (timeoutMethodParams != null)
{
// load the method param classes
timeoutMethodParamTypes = new Class<?>[timeoutMethodParams.size()];
int i = 0;
for (String paramClassName : timeoutMethodParams)
{
Class<?> methodParamClass = null;
try
{
methodParamClass = Class.forName(paramClassName, false, beanClass.getClassLoader());
}
catch (ClassNotFoundException cnfe)
{
throw new RuntimeException("Could not load method param class: " + paramClassName + " of timeout method");
}
timeoutMethodParamTypes[i++] = methodParamClass;
}
}
return timeoutMethodCallbackRequirements.getTimeoutMethod(beanClass, timeoutMethodName, timeoutMethodParamTypes);
}
}