/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt 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.mobicents.slee.runtime.sbb;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.slee.ActivityContextInterface;
import javax.slee.NoSuchObjectLocalException;
import javax.slee.SLEEException;
import javax.slee.TransactionRequiredLocalException;
import javax.slee.TransactionRolledbackLocalException;
import javax.transaction.SystemException;
import org.apache.log4j.Logger;
import org.mobicents.slee.SbbLocalObjectExt;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.deployment.interceptors.SbbLocalObjectInterceptor;
import org.mobicents.slee.container.eventrouter.EventRoutingTransactionData;
import org.mobicents.slee.container.sbb.SbbLocalObject;
import org.mobicents.slee.container.sbbentity.SbbEntity;
import org.mobicents.slee.container.sbbentity.SbbEntityID;
import org.mobicents.slee.container.transaction.SleeTransactionManager;
import org.mobicents.slee.container.transaction.TransactionContext;
import org.mobicents.slee.container.transaction.TransactionalAction;
import org.mobicents.slee.runtime.sbbentity.SbbEntityImpl;
/**
* This is a SLEE provided interface to the Sbb object. The SLEE uses this
* interface to allow sbbs to access local methods.
*
* The following is excerpted from Slee Spec
* <p>
* An SBB may define SBB specific local interface methods in an SBB specific
* local interface. The SBB specific local interface must be public and must
* extend, either directly or indirectly, the SbbLocalObject interface. All SBBs
* have an SBB local interface. If the SBB Developer does not provide an SBB
* local interface for an SBB, then the SBB local interface of the SBB is the
* generic SbbLocalObject interface. The names of the SBB specific local
* interface methods must not begin with sbb or ejb. The SLEE provides the
* implementation of the methods defined in the SBB local interface. More
* precisely, the SLEE provides a concrete class that implements each SBB local
* interface. An SBB local object is an instance of this class. The SLEE
* provided implementations of these methods delegate invocations on an SBB
* local object that represents an SBB entity to an SBB object that represents
* the SBB entity (if the SBB entity has not been removed). The SBB Developer
* provides the implementation of SBB Developer defined local interface methods
* declared in the SBB specific extension of the SbbLocalObject interface in the
* SBB abstract class. For each method defined by the SBB Developer, there must
* be a matching method in the SBB abstract class. The matching method must
* have:
* <ul>
* <li>The same name.
* <li>The same number of arguments, and same argument and return types.
* <li>The same set of exceptions in the throws clause.
* </ul>
*
* <p>
* All SBB local objects that represent an SBB entity that does not exist are
* invalid. An attempted invocation on an invalid SBB local object marks the
* current transaction for rollback and throws a
* javax.slee.TransactionRolledbackLocalException (a subclass of javax.slee.
* SLEEException). An SBB Developer defined local interface method is a
* mandatory transactional method (see Section 9.6.1). The SLEE throws a
* javax.slee.TransactionRequiredLocalException if an SBB Developer defined
* local interface method of an SBB local object is invoked without a valid
* transaction context.
*
* <p>
* This method may also throw a javax.slee.SLEEException if the method failed
* due to a SLEE level or system level failure. If the caller of this method
* receives this exception, the caller does not know, in general, whether the
* corresponding method implementation in the SBB abstract class was invoked.
* The caller also does not know if the transaction has been marked for
* rollback. However, the caller may determine the transaction status by using
* the getRollbackOnly method (see Section 6.10.3).
* <p>
* The SBB Developer defined SBB local interface methods must not throw
* java.rmi.Remote- Exception, any subclass of RemoteException, or any
* RuntimeException. For more information on exception handling for SBB local
* object invocations, see Section 6.9.
* <p>
* Parameters to local interface methods are passed by reference.
* <p>
* Note that the SbbLocalObject does not expose the methods of the
* javax.slee.Sbb interface, event handler methods, or the various callback
* methods. These methods are used by the SLEE to manage SBB object instances,
* deliver events, and handle callbacks.
*
* @author M. Ranganathan
* @author F. Moggia
* @author eduardomartins
*/
public class SbbLocalObjectImpl implements SbbLocalObject,
SbbLocalObjectConcrete {
/**
* Container where this resides -- for now this is the same location where
* the sbb resides.
*/
private static final SleeContainer sleeContainer = SleeContainer.lookupFromJndi();
private static final Logger logger = Logger.getLogger(SbbLocalObjectImpl.class);
private boolean rollbackOnly;
/*
* This flag is set to true if the sbb local object is removed (no longer
* valid)
*/
private boolean isRemoved;
private final SbbEntityImpl sbbEntity;
private ClassLoader contextClassLoader;
private final boolean trace;
/**
* used by class extensions to invoke custom sbb local object methods
*/
protected SbbLocalObjectInterceptor sbbLocalObjectInterceptor = new SbbLocalObjectInterceptor();
/**
* Constructor -- assume that the Sbb entity is co-located with the Sbb
* local object.
*
* @param sbbEntity --
* sbb entity for which this is a local object.
*/
public SbbLocalObjectImpl(final SbbEntityImpl sbbEntity) {
this.sbbEntity = sbbEntity;
if (sbbEntity.getSbbObject() == null){
if (System.getSecurityManager()!=null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
assignSbbObject();
return null;
}
});
}
else {
assignSbbObject();
}
}
trace = logger.isTraceEnabled();
if(trace)
logger.trace("SbbLocalObjectImpl(sbbEntity = "+sbbEntity.getSbbEntityId()+" )");
}
private void assignSbbObject() {
final Thread t = Thread.currentThread();
final ClassLoader cl = t.getContextClassLoader();
t.setContextClassLoader(sbbEntity.getSbbComponent().getClassLoader());
try {
sbbEntity.assignSbbObject();
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
finally {
t.setContextClassLoader(cl);
}
}
/* (non-Javadoc)
* @see org.mobicents.slee.runtime.sbb.SbbLocalObjectConcrete#getContextClassLoader()
*/
public ClassLoader getContextClassLoader() {
if (contextClassLoader == null) {
contextClassLoader = sbbEntity.getSbbComponent().getClassLoader();
}
return contextClassLoader;
}
/**
* @return the sbbEntity
*/
public SbbEntityImpl getSbbEntity() {
return sbbEntity;
}
/**
* Validates an invocation of the {@link SbbLocalObject}.
*
* @throws TransactionRolledbackLocalException
* @throws NoSuchObjectLocalException
* @throws SLEEException
*/
private void validateInvocation() throws TransactionRolledbackLocalException, NoSuchObjectLocalException, SLEEException {
// validate tx
sleeContainer.getTransactionManager().mandateTransaction();
// validate object
if (this.rollbackOnly) {
try {
sleeContainer.getTransactionManager().setRollbackOnly();
} catch (SystemException ex) {
throw new SLEEException("unable to set rollbackOnly in transaction manager", ex);
}
throw new TransactionRolledbackLocalException(
"Unable to proceed, transaction is set to rollback");
}
if (this.isRemoved)
throw new NoSuchObjectLocalException("sbb local object is removed");
}
/*
* (non-Javadoc)
* @see javax.slee.SbbLocalObject#getSbbPriority()
*/
public byte getSbbPriority() throws TransactionRequiredLocalException,
NoSuchObjectLocalException, SLEEException {
validateInvocation();
return sbbEntity.getPriority();
}
/*
* (non-Javadoc)
* @see javax.slee.SbbLocalObject#isIdentical(javax.slee.SbbLocalObject)
*/
public boolean isIdentical(javax.slee.SbbLocalObject obj)
throws TransactionRequiredLocalException, SLEEException {
validateInvocation();
return this.equals(obj);
}
/*
* (non-Javadoc)
* @see javax.slee.SbbLocalObject#remove()
*/
public void remove() throws TransactionRequiredLocalException,
NoSuchObjectLocalException, SLEEException {
if(trace)
logger.trace("remove()");
validateInvocation();
if (!sbbEntity.isReentrant()
&& sleeContainer.getTransactionManager().getTransactionContext()
.getInvokedNonReentrantSbbEntities().contains(sbbEntity.getSbbEntityId()))
throw new SLEEException(" re-entrancy not allowed ");
if (logger.isDebugEnabled()) {
logger.debug("nonSleeInitiatedCascadingRemoval : " + sbbEntity.getSbbId()
+ " entityID = " + sbbEntity.getSbbEntityId());
}
try {
sleeContainer.getSbbEntityFactory().removeSbbEntity(sbbEntity,false);
} catch (Throwable e) {
throw new SLEEException("Removal of the sbb entity failed",e);
}
try {
if (sleeContainer.getTransactionManager().getRollbackOnly()) {
final TransactionContext txContext = sleeContainer.getTransactionManager().getTransactionContext();
EventRoutingTransactionData ertd = txContext.getEventRoutingTransactionData();
txContext.getAfterRollbackActions()
.add(
new RolledBackAction(sbbEntity.getSbbEntityId(),ertd.getEventBeingDelivered().getEvent(),ertd.getAciReceivingEvent(), true));
}
} catch (Exception e) {
throw new SLEEException("Failed to check and possibly set rollback context of entity "+sbbEntity.getSbbEntityId(),e);
}
// I Think this should set isRemoved only to true but then test 323
// will fail.
// :-(
// Ralf: see above
this.rollbackOnly = true;
this.isRemoved = true;
}
/*
* (non-Javadoc)
* @see javax.slee.SbbLocalObject#setSbbPriority(byte)
*/
public void setSbbPriority(byte priority)
throws TransactionRequiredLocalException,
NoSuchObjectLocalException, SLEEException {
if(trace)
logger.trace("setSbbPriority( priority = "+priority+" )");
validateInvocation();
sbbEntity.setPriority(priority);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj != null && obj.getClass() == this.getClass()) {
final SbbLocalObjectImpl other = (SbbLocalObjectImpl) obj;
return this.sbbEntity.getSbbEntityId().equals(
other.sbbEntity.getSbbEntityId()) && this.isRemoved == other.isRemoved;
}
else {
return false;
}
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return sbbEntity.getSbbEntityId().hashCode();
}
/*
* (non-Javadoc)
* @see org.mobicents.slee.runtime.sbb.SbbLocalObjectConcrete#getSbbEntityId()
*/
public SbbEntityID getSbbEntityId() {
return sbbEntity.getSbbEntityId();
}
private static class RolledBackAction implements TransactionalAction {
final SbbEntityID sbbeId;
private Object event;
private ActivityContextInterface activityContextInterface;
private boolean removeRollback;
public RolledBackAction(SbbEntityID sbbeId,Object event,
ActivityContextInterface activityContextInterface,
boolean removeRollback) {
this.event = event;
this.activityContextInterface = activityContextInterface;
this.removeRollback = removeRollback;
this.sbbeId = sbbeId;
}
/*
* (non-Javadoc)
* @see org.mobicents.slee.runtime.transaction.TransactionalAction#execute()
*/
public void execute() {
SbbEntity sbbe = null;
final SleeTransactionManager txManager = sleeContainer.getTransactionManager();
try {
txManager.begin();
sbbe = sleeContainer.getSbbEntityFactory().getSbbEntity(sbbeId,true);
if (sbbe != null) {
sbbe.sbbRolledBack(event,activityContextInterface,removeRollback);
}
txManager.commit();
} catch (RuntimeException e) {
if (sbbe != null) {
sbbe.trashObject();
logger.error("Exception while executing RolledBackAction",e);
try {
if (txManager.getTransaction() != null)
txManager.rollback();
} catch (SystemException e1) {
logger.error(e1.getMessage(),e1);
}
}
} catch (Exception e) {
if (sbbe != null) {
sbbe.trashObject();
logger.error("Exception while executing RolledBackAction",e);
try {
if (txManager.getTransaction() != null)
txManager.rollback();
} catch (SystemException e1) {
logger.error(e1.getMessage(),e1);
}
}
}
}
}
@Override
public String toString() {
return new StringBuilder("SbbLocalObjectImpl[").append(sbbEntity != null ? sbbEntity.getSbbEntityId() : "null").append("]").toString();
}
// extension methods
@Override
public String getChildRelation() throws TransactionRequiredLocalException,
SLEEException {
validateInvocation();
return getSbbEntityId().getParentChildRelation();
}
@Override
public String getName() throws NoSuchObjectLocalException,
TransactionRequiredLocalException, SLEEException {
validateInvocation();
return getSbbEntityId().getName();
}
@Override
public SbbLocalObjectExt getParent() throws NoSuchObjectLocalException,
TransactionRequiredLocalException, SLEEException {
validateInvocation();
SbbLocalObjectExt parent = null;
final SbbEntityID parentSbbEntityID = getSbbEntityId().getParentSBBEntityID();
if (parentSbbEntityID != null) {
SbbEntity parentSbbEntity = sleeContainer.getSbbEntityFactory().getSbbEntity(parentSbbEntityID, false);
if (parentSbbEntity != null) {
parent = parentSbbEntity.getSbbLocalObject();
}
}
return parent;
}
}