/*
* 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.Start;
import org.jboss.ejb3.EJBContainer;
import org.jboss.ejb3.common.lang.ClassHelper;
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
*
* @author Jaikiran Pai
* @version $Revision: $
*/
public class AutoTimerInitializer
{
/**
* Logger
*/
private static Logger logger = Logger.getLogger(AutoTimerInitializer.class);
/**
* The EJB container
*/
private EJBContainer container;
/**
*
*/
public AutoTimerInitializer()
{
}
/**
* Creates a {@link AutoTimerInitializer} for a {@link EJBContainer}
* @param container {@link EJBContainer}
*/
public AutoTimerInitializer(EJBContainer container)
{
this.container = container;
}
/**
* Sets the {@link EJBContainer}
* @param container {@link EJBContainer}
*/
public void setContainer(EJBContainer container)
{
this.container = container;
}
/**
* Returns the {@link EJBContainer} for which this {@link AutoTimerInitializer} is responsible
* for creating auto timers
* @return
*/
public EJBContainer getContainer()
{
return this.container;
}
/**
* Creates any auto timers that are required for the {@link EJBContainer}, which this
* {@link AutoTimerInitializer} represents.
*/
@Start
public void initializeAutoTimers()
{
if (this.container == null)
{
throw new IllegalStateException("Cannot initialize auto-timers since container is not present");
}
JBossEnterpriseBeanMetaData enterpriseBeanMetaData = this.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;
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();
}
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 = this.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());
timerConfig.setInfo(autoTimerMetaData.getInfo());
NamedMethodMetaData timeoutMethodMetaData = autoTimerMetaData.getTimeoutMethod();
// get hold of the timeout method for this auto-timer
Method timeoutMethod = this.getTimeoutMethod(timeoutMethodMetaData, this.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();
List<Method> probableTimeoutMethods = ClassHelper.getAllMethodsByName(beanClass, timeoutMethodName);
if (probableTimeoutMethods == null || probableTimeoutMethods.isEmpty())
{
return null;
}
if (timeoutMethodParams == null)
{
if (probableTimeoutMethods.size() > 1)
{
throw new IllegalStateException("Found more than one timeout method with name: " + timeoutMethodName
+ " on bean " + beanClass);
}
// match found
return probableTimeoutMethods.get(0);
}
// load the method param classes
Class<?>[] 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;
}
for (Method method : probableTimeoutMethods)
{
Class<?>[] methodParamTypes = method.getParameterTypes();
if (equals(timeoutMethodParamTypes, methodParamTypes))
{
// match found
return method;
}
}
// no match found
return null;
}
private boolean equals(Class<?>[] params, Class<?>[] otherParams)
{
if (params.length != otherParams.length)
{
return false;
}
for (int i = 0; i < params.length; i++)
{
// param type doesn't match
if (!params[i].equals(otherParams[i]))
{
return false;
}
}
return true;
}
}