/**
* EasyBeans
* Copyright (C) 2006-2007 Bull S.A.S.
* Contact: easybeans@ow2.org
*
* This library 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 any later version.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* --------------------------------------------------------------------------
* $Id: EasyBeansEJBContext.java 5369 2010-02-24 14:58:19Z benoitf $
* --------------------------------------------------------------------------
*/
package org.ow2.easybeans.container;
import static org.ow2.easybeans.api.OperationState.AFTER_COMPLETION;
import static org.ow2.easybeans.api.OperationState.DEPENDENCY_INJECTION;
import static org.ow2.easybeans.api.OperationState.LIFECYCLE_CALLBACK_INTERCEPTOR;
import static org.ow2.util.ee.metadata.common.api.struct.ITransactionManagementType.BEAN;
import static org.ow2.util.ee.metadata.common.api.struct.ITransactionManagementType.CONTAINER;
import java.io.Serializable;
import java.security.Identity;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.ejb.EJBContext;
import javax.ejb.EJBException;
import javax.ejb.EJBHome;
import javax.ejb.EJBLocalHome;
import javax.ejb.ScheduleExpression;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import org.ow2.easybeans.api.Factory;
import org.ow2.easybeans.api.OperationState;
import org.ow2.easybeans.api.bean.info.ISecurityInfo;
import org.ow2.easybeans.api.container.EZBEJBContext;
import org.ow2.easybeans.container.session.stateful.StatefulSessionFactory;
import org.ow2.easybeans.container.session.stateless.StatelessSessionFactory;
import org.ow2.easybeans.security.propagation.context.SecurityCurrent;
import org.ow2.easybeans.transaction.JTransactionManager;
import org.ow2.easybeans.transaction.interceptors.CMTSupportsTransactionInterceptor;
import org.ow2.util.ee.metadata.common.api.struct.ITransactionManagementType;
import org.ow2.util.ee.metadata.common.api.xml.struct.ISecurityRoleRef;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;
/**
* Class implementing the EJBContext interface.
* It is extended for Session context or MessageDriven Context
* @param <FactoryType> The type of bean managed.
* @author Florent Benoit
*/
public class EasyBeansEJBContext<FactoryType extends Factory<?, ?>> implements EZBEJBContext<FactoryType>, EJBContext {
/**
* Logger.
*/
private final Log logger = LogFactory.getLog(EasyBeansEJBContext.class);
/**
* java:comp/env prefix.
*/
private static final String JAVA_COMP_ENV = "java:comp/env/";
/**
* Reference to the transaction manager.
*/
private TransactionManager transactionManager = null;
/**
* Type of transaction.
*/
private ITransactionManagementType transactionManagementType = null;
/**
* Bean is using run-as ?
*/
private boolean runAsBean = false;
/**
* Link to the factory.
*/
private FactoryType easyBeansFactory = null;
/**
* Timer service.
*/
private TimerService timerService = null;
/**
* Builds a default EJB Context implementation.
* @param easyBeansFactory used to get the transaction management type.
*/
public EasyBeansEJBContext(final FactoryType easyBeansFactory) {
this.easyBeansFactory = easyBeansFactory;
this.transactionManagementType = easyBeansFactory.getBeanInfo().getTransactionManagementType();
this.runAsBean = easyBeansFactory.getBeanInfo().getSecurityInfo().getRunAsRole() != null;
// Get Transaction manager
this.transactionManager = JTransactionManager.getTransactionManager();
// Get timer service
this.timerService = easyBeansFactory.getTimerService();
// If the component is not here, use a dummy implementation
if (this.timerService == null) {
this.timerService = new MissingTimerService();
}
}
/**
* Obtain the enterprise bean's remote home interface.
* @return The enterprise bean's remote home interface.
* @throws IllegalStateException if the enterprise bean does not have a
* remote home interface.
*/
@Override
public EJBHome getEJBHome() throws IllegalStateException {
throw new IllegalStateException("No Home");
}
/**
* Obtain the enterprise bean's local home interface.
* @return The enterprise bean's local home interface.
* @throws IllegalStateException - if the enterprise bean does not have a
* local home interface.
*/
@Override
public EJBLocalHome getEJBLocalHome() throws IllegalStateException {
throw new IllegalStateException("No Local Home");
}
/**
* Use the JNDI naming context java:comp/env to access enterprise bean's
* environment. Obtain the enterprise bean's environment properties. Note:
* If the enterprise bean has no environment properties this method returns
* an empty java.util.Properties object. This method never returns null.
* @return The environment properties for the enterprise bean.
*/
@Override
@Deprecated
public Properties getEnvironment() {
throw new UnsupportedOperationException();
}
/**
* Use Principal getCallerPrincipal() instead. Obtain the
* java.security.Identity of the caller. This method is deprecated in EJB
* 1.1. The Container is allowed to return alway null from this method. The
* enterprise bean should use the getCallerPrincipal method instead.
* @return The Identity object that identifies the caller.
*/
@Override
@Deprecated
public Identity getCallerIdentity() {
throw new UnsupportedOperationException();
}
/**
* Obtain the java.security.Principal that identifies the caller.
* @return The Principal object that identifies the caller. This method
* never returns null.
*/
@Override
public Principal getCallerPrincipal() {
// Disallowed from dependency injection and from stateless lifecycle
// callbacks
OperationState operationState = getFactory().getOperationState();
if (DEPENDENCY_INJECTION == operationState
|| (LIFECYCLE_CALLBACK_INTERCEPTOR == operationState && getFactory() instanceof StatelessSessionFactory)) {
throw new IllegalStateException("The getCallerPrincipal() method cannot be called within the operation state '"
+ operationState + "'.");
}
return SecurityCurrent.getCurrent().getSecurityContext().getCallerPrincipal(this.runAsBean);
}
/**
* Use boolean isCallerInRole(String roleName) instead. Test if the caller
* has a given role. This method is deprecated in EJB 1.1. The enterprise
* bean should use the isCallerInRole(String roleName) method instead.
* @param role The java.security.Identity of the role to be tested.
* @return True if the caller has the specified role.
*/
@Override
@Deprecated
public boolean isCallerInRole(final Identity role) {
throw new UnsupportedOperationException();
}
/**
* Test if the caller has a given security role.
* @param roleName The name of the security role. The role must be one of
* the security roles that is defined in the deployment descriptor.
* @return True if the caller has the specified role.
*/
@Override
public boolean isCallerInRole(final String roleName) {
// Disallowed from dependency injection and from stateless lifecycle
// callbacks
OperationState operationState = getFactory().getOperationState();
if (DEPENDENCY_INJECTION == operationState
|| (LIFECYCLE_CALLBACK_INTERCEPTOR == operationState && getFactory() instanceof StatelessSessionFactory)) {
throw new IllegalStateException("The isCallerInRole() method cannot be called within the operation state '"
+ operationState + "'.");
}
// Get list of declared roles for this bean.
ISecurityInfo securityInfo = this.easyBeansFactory.getBeanInfo().getSecurityInfo();
List<String> declaredRoles = securityInfo.getDeclaredRoles();
if (declaredRoles == null) {
declaredRoles = new ArrayList<String>();
}
// Add also all security roles declared as security-role-ref
List<ISecurityRoleRef> securityRoleRefList = securityInfo.getSecurityRoleRefList();
if (securityRoleRefList != null) {
for (ISecurityRoleRef securityRoleRef : securityRoleRefList) {
if (!declaredRoles.contains(securityRoleRef.getRoleName())) {
declaredRoles.add(securityRoleRef.getRoleName());
}
}
}
// Not contained ?
if (!declaredRoles.contains(roleName)) {
this.logger.debug("No security-role with role name ''{0}'' was declared for the bean ''{1}''", roleName,
this.easyBeansFactory.getBeanInfo().getName());
return false;
}
// Check with JACC manager
boolean inRole = this.easyBeansFactory.getContainer().getPermissionManager().isCallerInRole(this.easyBeansFactory
.getBeanInfo().getName(), roleName, this.runAsBean);
return inRole;
}
/**
* Obtain the transaction demarcation interface. Only enterprise beans with
* bean-managed transactions are allowed to to use the UserTransaction
* interface. As entity beans must always use container-managed
* transactions, only session beans with bean-managed transactions are
* allowed to invoke this method.
* @return The UserTransaction interface that the enterprise bean instance
* can use for transaction demarcation.
* @throws java.lang.IllegalStateException - The Container throws the
* exception if the instance is not allowed to use the
* UserTransaction interface (i.e. the instance is of a bean with
* container-managed transactions).
*/
@Override
public UserTransaction getUserTransaction() throws IllegalStateException {
OperationState operationState = getFactory().getOperationState();
if (DEPENDENCY_INJECTION == operationState) {
throw new IllegalStateException("The getUserTransaction() method cannot be called within the operation state '"
+ operationState + "'.");
}
if (this.transactionManagementType == CONTAINER) {
throw new IllegalStateException("This bean is not allowed to use getUserTransaction() "
+ " method as it is in ContainerManagedTransaction");
}
return (UserTransaction) this.transactionManager;
}
/**
* Mark the current transaction for rollback. The transaction will become
* permanently marked for rollback. A transaction marked for rollback can
* never commit. Only enterprise beans with container-managed transactions
* are allowed to use this method.
* @throws java.lang.IllegalStateException - The Container throws the
* exception if the instance is not allowed to use this method (i.e.
* the instance is of a bean with bean-managed transactions).
*/
@Override
public void setRollbackOnly() throws IllegalStateException {
OperationState operationState = getFactory().getOperationState();
if (DEPENDENCY_INJECTION == operationState || LIFECYCLE_CALLBACK_INTERCEPTOR == operationState
|| AFTER_COMPLETION == operationState) {
throw new IllegalStateException("The setRollbackOnly() method cannot be called within the operation state '"
+ operationState + "'.");
}
if (this.transactionManagementType == BEAN) {
throw new IllegalStateException("This bean is not allowed to use setRollbackOnly() "
+ " method as it is in BeanManagedTransaction");
}
Map<String, Object> contextData = getContextData();
if (contextData != null) {
Object value = contextData.get(CMTSupportsTransactionInterceptor.class.getName());
if (value != null && ((Boolean) value).booleanValue()) {
throw new IllegalStateException("This bean is not allowed to use getRollbackOnly() "
+ " method as it is in a SUPPORTS call");
}
}
// Check if there is a transaction, as it is mandatory
try {
if (this.transactionManager.getTransaction() == null) {
throw new IllegalStateException("Cannot use setRollbackOnly() outside transaction");
}
} catch (SystemException e) {
throw new IllegalStateException("Cannot get transaction on transaction manager", e);
}
try {
this.transactionManager.setRollbackOnly();
} catch (SystemException e) {
throw new RuntimeException("setRollbackOnly() raised an unexpected exception:", e);
}
}
/**
* Test if the transaction has been marked for rollback only. An enterprise
* bean instance can use this operation, for example, to test after an
* exception has been caught, whether it is fruitless to continue
* computation on behalf of the current transaction. Only enterprise beans
* with container-managed transactions are allowed to use this method.
* @return True if the current transaction is marked for rollback, false
* otherwise.
* @throws java.lang.IllegalStateException - The Container throws the
* exception if the instance is not allowed to use this method (i.e.
* the instance is of a bean with bean-managed transactions).
*/
@Override
public boolean getRollbackOnly() throws IllegalStateException {
OperationState operationState = getFactory().getOperationState();
if (DEPENDENCY_INJECTION == operationState || LIFECYCLE_CALLBACK_INTERCEPTOR == operationState
|| AFTER_COMPLETION == operationState) {
throw new IllegalStateException("The getRollbackOnly() method cannot be called within the operation state '"
+ operationState + "'.");
}
if (this.transactionManagementType == BEAN) {
throw new IllegalStateException("This bean is not allowed to use getRollbackOnly() "
+ " method as it is in BeanManagedTransaction");
}
Map<String, Object> contextData = getContextData();
if (contextData != null) {
Object value = contextData.get(CMTSupportsTransactionInterceptor.class.getName());
if (value != null && ((Boolean) value).booleanValue()) {
throw new IllegalStateException("This bean is not allowed to use getRollbackOnly() "
+ " method as it is in a SUPPORTS call");
}
}
try {
switch (this.transactionManager.getStatus()) {
case Status.STATUS_MARKED_ROLLBACK:
case Status.STATUS_ROLLING_BACK:
return true;
case Status.STATUS_ACTIVE:
case Status.STATUS_COMMITTING:
case Status.STATUS_PREPARED:
case Status.STATUS_PREPARING:
return false;
case Status.STATUS_ROLLEDBACK:
throw new IllegalStateException("Transaction already rolled back");
case Status.STATUS_COMMITTED:
throw new IllegalStateException("Transaction already committed");
case Status.STATUS_NO_TRANSACTION:
case Status.STATUS_UNKNOWN:
throw new IllegalStateException("Cannot getRollbackOnly outside transaction");
default:
throw new IllegalStateException("Invalid status");
}
} catch (SystemException e) {
throw new IllegalStateException("Cannot get transaction status", e);
}
}
/**
* Get access to the EJB Timer Service.
* @return Timer service.
* @throws java.lang.IllegalStateException The Container throws the
* exception if the instance is not allowed to use this method (e.g.
* if the bean is a stateful session bean)
*/
@Override
public TimerService getTimerService() throws IllegalStateException {
// Disallowed from stateful
if (getFactory() instanceof StatefulSessionFactory) {
throw new IllegalStateException("The getTimerService() method cannot be called from a stateful session bean.");
}
OperationState operationState = getFactory().getOperationState();
if (DEPENDENCY_INJECTION == operationState) {
throw new IllegalStateException("The getTimerService() method cannot be called within the operation state '"
+ operationState + "'.");
}
return getInternalTimerService();
}
/**
* Get access to the EJB Timer Service.
* @return Timer service.
*/
@Override
public TimerService getInternalTimerService() {
return this.timerService;
}
/**
* @return string representation.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
// classname
sb.append(this.getClass().getName().substring(this.getClass().getPackage().getName().length() + 1));
return sb.toString();
}
/**
* Lookup object with given name.
* @param name given name
* @return result of the lookup
*/
@Override
public Object lookup(final String name) {
if (name == null) {
throw new IllegalArgumentException("Invalid resource name used for lookup '" + name + "'.");
}
// Search in java:comp/env first
try {
return new InitialContext().lookup(JAVA_COMP_ENV + name);
} catch (NamingException ne) {
// try in registry
try {
return new InitialContext().lookup(name);
} catch (NamingException e) {
throw new IllegalArgumentException("Lookup on '" + name + "' was not found");
}
}
}
/**
* Gets the factory of this context.
* @return factory used by this context.
*/
@Override
public FactoryType getFactory() {
return this.easyBeansFactory;
}
/**
* Enables a business method, lifecycle callback method, or timeout method to retrieve any interceptor/webservices context
* associated with its invocation.
* @return the shared context
* @since EJB 3.1 version.
*/
@Override
public Map<String, Object> getContextData() {
return this.easyBeansFactory.getContextDataThreadLocal().get();
}
/**
* Implementation of Timer Service that throws exception as
* it means that the timer component is missing.
* @author Florent Benoit
*/
class MissingTimerService implements TimerService {
/**
* Create an interval timer whose first expiration occurs after a
* specified duration, and whose subsequent expirations occur after a
* specified interval.
* @param initialDuration - The number of milliseconds that must elapse
* before the first timer expiration notification.
* @param intervalDuration - The number of milliseconds that must elapse
* between timer expiration notifications. Expiration
* notifications are scheduled relative to the time of the first
* expiration. If expiration is delayed(e.g. due to the
* interleaving of other method calls on the bean) two or more
* expiration notifications may occur in close succession to
* "catch up".
* @param info - Application information to be delivered along with the
* timer expiration. This can be null.
* @return The newly created Timer.
* @throws IllegalArgumentException - If initialDuration is negative, or
* intervalDuration is negative.
* @throws IllegalStateException - If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException - If this method could not complete due to a
* system-level failure.
*/
@Override
public Timer createTimer(final Date initialDuration, final long intervalDuration, final Serializable info)
throws IllegalArgumentException, IllegalStateException, EJBException {
throw new IllegalStateException("No timer component was found in the EasyBeans components");
}
/**
* Create a single-action timer that expires at a given point in time.
* @param expiration - The point in time at which the timer must expire.
* @param info - Application information to be delivered along with the
* timer expiration notification. This can be null.
* @return The newly created Timer.
* @throws IllegalArgumentException - If expiration is null, or
* expiration.getTime() is negative.
* @throws IllegalStateException - If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException - If this method could not complete due to a
* system-level failure.
*/
@Override
public Timer createTimer(final Date expiration, final Serializable info) throws IllegalArgumentException,
IllegalStateException, EJBException {
throw new IllegalStateException("No timer component was found in the EasyBeans components");
}
/**
* Create an interval timer whose first expiration occurs after a
* specified duration, and whose subsequent expirations occur after a
* specified interval.
* @param initialDuration - The number of milliseconds that must elapse
* before the first timer expiration notification.
* @param intervalDuration - The number of milliseconds that must elapse
* between timer expiration notifications. Expiration
* notifications are scheduled relative to the time of the first
* expiration. If expiration is delayed(e.g. due to the
* interleaving of other method calls on the bean) two or more
* expiration notifications may occur in close succession to
* "catch up".
* @param info - Application information to be delivered along with the
* timer expiration. This can be null.
* @return The newly created Timer.
* @throws IllegalArgumentException - If initialDuration is negative, or
* intervalDuration is negative.
* @throws IllegalStateException - If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException - If this method could not complete due to a
* system-level failure.
*/
@Override
public Timer createTimer(final long initialDuration, final long intervalDuration, final Serializable info)
throws IllegalArgumentException, IllegalStateException, EJBException {
throw new IllegalStateException("No timer component was found in the EasyBeans components");
}
/**
* Create a single-action timer that expires after a specified duration.
* @param duration - The number of milliseconds that must elapse before
* the timer expires.
* @param info - Application information to be delivered along with the
* timer expiration notification. This can be null.
* @return The newly created Timer.
* @throws IllegalArgumentException - If duration is negative
* @throws IllegalStateException - If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException - If this method fails due to a system-level
* failure.
*/
@Override
public Timer createTimer(final long duration, final Serializable info) throws IllegalArgumentException,
IllegalStateException, EJBException {
throw new IllegalStateException("No timer component was found in the EasyBeans components");
}
/**
* Get all the active timers associated with this bean.
* @return A collection of javax.ejb.Timer objects.
* @throws IllegalStateException - If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException - If this method could not complete due to a
* system-level failure.
*/
@Override
public Collection<Timer> getTimers() throws IllegalStateException, EJBException {
return Collections.emptyList();
}
/**
* Create a single-action timer that expires after a specified duration.
* @param duration the number of milliseconds that must elapse before
* the timer expires.
* @param timerConfig timer configuration.
* @return the newly created Timer.
* @throws IllegalArgumentException If duration is negative
* @throws IllegalStateException If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException If this method fails due to a system-level
* failure.
* @since EJB 3.1 version.
*/
@Override
public Timer createSingleActionTimer(final long duration, final TimerConfig timerConfig)
throws IllegalArgumentException, IllegalStateException, EJBException {
throw new IllegalStateException("No timer component was found in the EasyBeans components");
}
/**
* Create a single-action timer that expires at a given point in time.
* @param expiration the point in time at which the timer must expire.
* @param timerConfig timer configuration.
* @return the newly created Timer.
* @throws IllegalArgumentException If expiration is null or
* expiration.getTime() is negative.
* @throws IllegalStateException If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException If this method could not complete due to a
* system-level failure.
* @since EJB 3.1 version.
*/
@Override
public Timer createSingleActionTimer(final Date expiration, final TimerConfig timerConfig)
throws IllegalArgumentException, IllegalStateException, EJBException {
throw new IllegalStateException("No timer component was found in the EasyBeans components");
}
/**
* Create an interval timer whose first expiration occurs after a
* specified duration, and whose subsequent expirations occur after a
* specified interval.
* @param initialDuration The number of milliseconds that must elapse
* before the first timer expiration notification.
* @param intervalDuration The number of milliseconds that must elapse
* between timer expiration notifications. Expiration
* notifications are scheduled relative to the time of the first
* expiration. If expiration is delayed (e.g. due to the
* interleaving of other method calls on the bean), two or more
* expiration notifications may occur in close succession to
* "catch up".
* @param timerConfig timer configuration
* @return the newly created Timer.
* @throws IllegalArgumentException If initialDuration is negative or
* intervalDuration is negative.
* @throws IllegalStateException If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException If this method could not complete due to a
* system-level failure.
* @since EJB 3.1 version.
*/
@Override
public Timer createIntervalTimer(final long initialDuration, final long intervalDuration, final TimerConfig timerConfig)
throws IllegalArgumentException, IllegalStateException, EJBException {
throw new IllegalStateException("No timer component was found in the EasyBeans components");
}
/**
* Create an interval timer whose first expiration occurs at a given
* point in time and whose subsequent expirations occur after a
* specified interval.
* @param initialExpiration the point in time at which the first timer
* expiration must occur.
* @param intervalDuration the number of milliseconds that must elapse
* between timer expiration notifications. Expiration
* notifications are scheduled relative to the time of the first
* expiration. If expiration is delayed (e.g. due to the
* interleaving of other method calls on the bean), two or more
* expiration notifications may occur in close succession to
* "catch up".
* @param timerConfig timer configuration.
* @return the newly created Timer.
* @throws IllegalArgumentException If initialExpiration is null, if
* initialExpiration.getTime() is negative, or if
* intervalDuration is negative.
* @throws IllegalStateException If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException If this method could not complete due to a
* system-level failure.
* @since EJB 3.1 version.
*/
@Override
public Timer createIntervalTimer(final Date initialExpiration, final long intervalDuration,
final TimerConfig timerConfig) throws IllegalArgumentException, IllegalStateException, EJBException {
throw new IllegalStateException("No timer component was found in the EasyBeans components");
}
/**
* Create a calendar-based timer based on the input schedule expression.
* @param schedule a schedule expression describing the timeouts for
* this timer.
* @return the newly created Timer.
* @throws IllegalArgumentException If Schedule represents an invalid
* schedule expression.
* @throws IllegalStateException If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException If this method could not complete due to a
* system-level failure.
* @since EJB 3.1 version.
*/
@Override
public Timer createCalendarTimer(final ScheduleExpression schedule) throws IllegalArgumentException,
IllegalStateException, EJBException {
throw new IllegalStateException("No timer component was found in the EasyBeans components");
}
/**
* Create a calendar-based timer based on the input schedule expression.
* @param schedule a schedule expression describing the timeouts for
* this timer.
* @param timerConfig timer configuration.
* @return the newly created Timer.
* @throws IllegalArgumentException If Schedule represents an invalid
* schedule expression.
* @throws IllegalStateException If this method is invoked while the
* instance is in a state that does not allow access to this
* method.
* @throws EJBException If this method could not complete due to a
* system-level failure.
* @since EJB 3.1 version.
*/
@Override
public Timer createCalendarTimer(final ScheduleExpression schedule, final TimerConfig timerConfig)
throws IllegalArgumentException, IllegalStateException, EJBException {
throw new IllegalStateException("No timer component was found in the EasyBeans components");
}
}
}