/* * Created on Feb 3, 2005 * * The Mobicents Open SLEE Project * * A SLEE for the People * * The source code contained in this file is in in the public domain. * It can be used in any project or product without prior permission, * license or royalty payments. There is no claim of correctness and * NO WARRANTY OF ANY KIND provided with this code. */ package org.mobicents.slee.runtime.sbbentity; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import javax.slee.SLEEException; import javax.slee.SbbID; import javax.slee.ServiceID; import javax.transaction.SystemException; import javax.transaction.TransactionRequiredException; import org.apache.log4j.Logger; import org.mobicents.slee.container.MobicentsUUIDGenerator; import org.mobicents.slee.container.SleeContainer; import org.mobicents.slee.runtime.transaction.SleeTransactionManager; /** * * SbbEntityFactory -- implements a map from SBB Entity Id to SBB Entity * instances. This is the sole place where Sbb Entities are created and * destroyed. We keep a map of SBB Entity ID to SBB Entity here and return Sbb * Entities that are stored in this map. * * @author F.Moggia * @author M. Ranganathan * @author Tim Fox ( streamlining ) * @author eduardomartins */ public class SbbEntityFactory { private static final Logger logger = Logger .getLogger(SbbEntityFactory.class); /** * * @return */ private static String genId() { return MobicentsUUIDGenerator.getInstance().createUUID(); } private static final SleeContainer sleeContainer = SleeContainer .lookupFromJndi(); protected static final SbbEntityLockFacility lockFacility = new SbbEntityLockFacility(sleeContainer); /** * Creates a new non root sbb entity. * * @param sbbId * @param svcId * @param parentSbbEntityId * @param parentChildRelation * @param rootSbbEntityId * @param convergenceName * @return */ public static SbbEntity createSbbEntity(SbbID sbbId, ServiceID svcId, String parentSbbEntityId, String parentChildRelation, String rootSbbEntityId, String convergenceName) { try { final String sbbeId = genId(); return _createSbbEntity(sbbeId, sbbId, svcId, parentSbbEntityId, parentChildRelation, rootSbbEntityId, convergenceName); } catch (Throwable ex) { throw new SLEEException( "Exception in creating non root sbb entity!", ex); } } /** * Creates a new root sbb entity * * @param sbbId * @param svcId * @param convergenceName * @return */ public static SbbEntity createRootSbbEntity(SbbID sbbId, ServiceID svcId, String convergenceName) { try { final String sbbeId = genId(); return _createSbbEntity(sbbeId, sbbId, svcId, null, null, sbbeId, convergenceName); } catch (Throwable ex) { throw new SLEEException("Exception in creating root sbb entity!", ex); } } /** * * @param sbbeId * @param sbbId * @param svcId * @param parentSbbEntityId * @param parentChildRelation * @param rootSbbEntityId * @param convergenceName * @return * @throws Exception */ private static SbbEntity _createSbbEntity(final String sbbeId, SbbID sbbId, ServiceID svcId, String parentSbbEntityId, String parentChildRelation, String rootSbbEntityId, String convergenceName) throws Exception { final SleeTransactionManager txManager = sleeContainer .getTransactionManager(); // get lock final ReentrantLock lock = lockFacility.get(sbbeId); lockOrFail(lock,sbbeId); // we hold the lock now // create sbb entity final SbbEntityImmutableData sbbEntityImmutableData = new SbbEntityImmutableData(sbbId, svcId, parentSbbEntityId, parentChildRelation, rootSbbEntityId, convergenceName); final SbbEntity sbbEntity = new SbbEntity(sbbeId, sbbEntityImmutableData); // store it in the tx, we need to do it due to sbb local object and // current storing in sbb entity per tx storeSbbEntityInTx(sbbEntity, txManager); // add tx actions to unlock it when the tx ends try { SbbEntityUnlockTransactionalAction rollbackAction = new SbbEntityUnlockTransactionalAction(sbbEntity,lock,true,true); SbbEntityUnlockTransactionalAction commitAction = new SbbEntityUnlockTransactionalAction(sbbEntity,lock,true,false); txManager.addAfterRollbackAction(rollbackAction); txManager.addAfterCommitAction(commitAction); } catch (Throwable e) { throw new SLEEException(e.getMessage(),e); } return sbbEntity; } /** * * @param sbbeId * @return */ public static SbbEntity getSbbEntityWithoutLock(String sbbeId) { return _getSbbEntity(sbbeId, false); } /** * * @param sbbeId * @return */ public static SbbEntity getSbbEntity(String sbbeId) { return _getSbbEntity(sbbeId, true); } /** * Call this function when you want to get an instantiated SbbEntity from * the cache. * * @param sbbeId * @return */ private static SbbEntity _getSbbEntity(String sbbeId,boolean useLock) { if (sbbeId == null) throw new NullPointerException("Null Sbbeid"); /* * NB!! We must not use a map to cache sbb entities. There can be * multiple active transactions each of which accesses the same sbb * entity at any one time in the SLEE. Therefore, by using a map to * store the sbb entities one tx can see the transactional state of the * other tx before it has committed. I.e. we would have no transaction * isolation. Instead, if we want to cache the sbb entity for the * lifetime of the tx for performance reasons, we need to store in a * *per transaction* cache */ SleeTransactionManager txMgr = sleeContainer.getTransactionManager(); // Look for it in the per transaction cache SbbEntity sbbEntity = getSbbEntityFromTx(sbbeId, txMgr); if (sbbEntity == null) { // not found in tx SbbEntityUnlockTransactionalAction rollbackAction = null; SbbEntityUnlockTransactionalAction commitAction = null; if (useLock) { rollbackAction = new SbbEntityUnlockTransactionalAction(false,true); commitAction = new SbbEntityUnlockTransactionalAction(false,false); final ReentrantLock lock = lockFacility.get(sbbeId); if (lock != null) { lockOrFail(lock,sbbeId); rollbackAction.setReentrantLock(lock); commitAction.setReentrantLock(lock); try { txMgr.addAfterRollbackAction(rollbackAction); txMgr.addAfterCommitAction(commitAction); } catch (SystemException e) { throw new SLEEException(e.getMessage(), e); } } } if (logger.isDebugEnabled()) logger.debug("Loading sbb entity " + sbbeId + " from cache"); sbbEntity = new SbbEntity(sbbeId); if (useLock) { rollbackAction.setSbbEntity(sbbEntity); commitAction.setSbbEntity(sbbEntity); } // store it in the tx, we need to do it due to sbb local object and // current storing in sbb entity per tx storeSbbEntityInTx(sbbEntity, txMgr); } return sbbEntity; } /** * Removes the specified sbb entity. The sbb class loader is used on this * operation. * * @param sbbEntity * the sbb entity to remove * @param removeFromParent * indicates if the entity should be remove from it's parent also * @throws TransactionRequiredException * @throws SystemException */ public static void removeSbbEntity(SbbEntity sbbEntity, boolean removeFromParent) throws TransactionRequiredException, SystemException { ClassLoader oldClassLoader = Thread.currentThread() .getContextClassLoader(); try { Thread.currentThread().setContextClassLoader( sbbEntity.getSbbComponent().getClassLoader()); removeSbbEntityWithCurrentClassLoader(sbbEntity, removeFromParent); } finally { // restore old class loader Thread.currentThread().setContextClassLoader(oldClassLoader); } } /** * Removes the specified sbb entity but without changing to sbb's class * loader first. * * @param sbbEntity * the sbb entity to remove * @param removeFromParent * indicates if the entity should be remove from it's parent also * @throws TransactionRequiredException * @throws SystemException */ public static void removeSbbEntityWithCurrentClassLoader( SbbEntity sbbEntity, boolean removeFromParent) throws TransactionRequiredException, SystemException { // remove entity sbbEntity.remove(removeFromParent); // remove from tx data sleeContainer.getTransactionManager().getTransactionContext().getData().remove( sbbEntity.getSbbEntityId()); } /** * Removes the specified sbb entity. * * @param sbbEntity * the sbb entity to remove * @param removeFromParent * indicates if the entity should be remove from it's parent also * @throws TransactionRequiredException * @throws SystemException */ public static void removeSbbEntity(String sbbEntityId, boolean removeFromParent) throws TransactionRequiredException, SystemException { removeSbbEntity(getSbbEntity(sbbEntityId), removeFromParent); } // --- helpers /** * * @param sbbEntity * @param txManager * @throws SystemException */ @SuppressWarnings("unchecked") private static void storeSbbEntityInTx(SbbEntity sbbEntity, SleeTransactionManager txManager) { try { txManager.getTransactionContext().getData().put( sbbEntity.getSbbEntityId(), sbbEntity); } catch (SystemException e) { throw new SLEEException(e.getMessage(), e); } } /** * * @param sbbeId * @param txManager * @return * @throws SystemException */ private static SbbEntity getSbbEntityFromTx(String sbbeId, SleeTransactionManager txManager) { try { return (SbbEntity) txManager.getTransactionContext().getData().get( sbbeId); } catch (SystemException e) { throw new SLEEException(e.getMessage(), e); } } /** * * @param lock * @param sbbeId * @throws SLEEException */ private static void lockOrFail(ReentrantLock lock, String sbbeId) throws SLEEException { if (logger.isDebugEnabled()) { logger.debug(Thread.currentThread()+" trying to acquire lock "+lock+" for sbb entity with id "+sbbeId); } boolean locked; try { locked = lock.tryLock(10, TimeUnit.SECONDS); } catch (Throwable e) { throw new SLEEException(e.getMessage(),e); } if (!locked) { throw new SLEEException("timeout while acquiring lock "+lock+" for sbb entity with id "+sbbeId); } if (logger.isDebugEnabled()) { logger.debug(Thread.currentThread()+" acquired lock "+lock+" for sbb entity with id "+sbbeId); } } /** * * @return */ public static Set<String> getSbbEntityIDs() { final SbbEntityFactoryCacheData cacheData = new SbbEntityFactoryCacheData(sleeContainer.getCluster()); if (cacheData.exists()) { return cacheData.getSbbEntities(); } else { return null; } } }