/* * 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.activity; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.slee.Address; import javax.slee.EventTypeID; import javax.slee.SLEEException; import javax.slee.ServiceID; import javax.slee.facilities.TimerID; import javax.slee.resource.ActivityFlags; import javax.slee.resource.ActivityIsEndingException; import org.apache.log4j.Logger; import org.infinispan.tree.Node; import org.mobicents.slee.container.SleeContainer; import org.mobicents.slee.container.activity.ActivityContext; import org.mobicents.slee.container.activity.ActivityContextHandle; import org.mobicents.slee.container.activity.ActivityEventQueueManager; import org.mobicents.slee.container.activity.ActivityType; import org.mobicents.slee.container.event.EventContext; import org.mobicents.slee.container.event.EventProcessingFailedCallback; import org.mobicents.slee.container.event.EventProcessingSucceedCallback; import org.mobicents.slee.container.event.EventUnreferencedCallback; import org.mobicents.slee.container.facilities.ActivityContextNamingFacility; import org.mobicents.slee.container.facilities.TimerFacility; import org.mobicents.slee.container.resource.ResourceAdaptorActivityContextHandle; import org.mobicents.slee.container.sbbentity.SbbEntityID; import org.mobicents.slee.container.service.ServiceActivityHandle; import org.mobicents.slee.container.transaction.TransactionContext; import org.mobicents.slee.runtime.event.ActivityEndEventUnreferencedCallback; import org.mobicents.slee.runtime.event.CommitEventContextAction; import org.mobicents.slee.runtime.event.RollbackEventContextAction; /** * Create one of these when a new SipTransaction is seen by the stack. Call the * Slee Endpoint. This is a cached object so it is created from a factory * interface. * * @author M. Ranganathan * @author F.Moggia * @author Tim - tx stuff * @author Ivelin Ivanov * @author eduardomartins * */ public class ActivityContextImpl implements ActivityContext { static final SleeContainer sleeContainer = SleeContainer.lookupFromJndi(); private static final Logger logger = Logger .getLogger(ActivityContext.class); // --- map keys for attributes cached private static final String NODE_MAP_KEY_ACTIVITY_FLAGS = "flags"; private static final String NODE_MAP_KEY_LAST_ACCESS = "time"; /** * the handle for this ac */ private final ActivityContextHandle activityContextHandle; /** * the data stored in cache for this ac */ protected final ActivityContextCacheData cacheData; private final ActivityContextFactoryImpl factory; private Integer flags; private final ActivityContextReferencesHandler acReferencesHandler; public ActivityContextImpl( final ActivityContextHandle activityContextHandle, ActivityContextCacheData cacheData, boolean updateAccessTime, Integer activityFlags, ActivityContextFactoryImpl factory) { this.activityContextHandle = activityContextHandle; this.factory = factory; this.cacheData = cacheData; // ac creation, create cache data and set activity flags this.cacheData.create(); this.cacheData.putObject(NODE_MAP_KEY_ACTIVITY_FLAGS, activityFlags); this.flags = activityFlags; // set access time if needed if (updateAccessTime) { updateLastAccessTime(true); } // setup references handler if needed if (ActivityFlags.hasRequestSleeActivityGCCallback(activityFlags) && !isEnding()) { acReferencesHandler = new ActivityContextReferencesHandler(this); } else { acReferencesHandler = null; } } public ActivityContextImpl(ActivityContextHandle activityContextHandle, ActivityContextCacheData cacheData, boolean updateAccessTime, ActivityContextFactoryImpl factory) { this.activityContextHandle = activityContextHandle; this.factory = factory; this.cacheData = cacheData; // set access time if needed if (cacheData.exists() && updateAccessTime) { updateLastAccessTime(false); } // setup references handler if needed if (ActivityFlags.hasRequestSleeActivityGCCallback(getActivityFlags()) && !isEnding()) { acReferencesHandler = new ActivityContextReferencesHandler(this); } else { acReferencesHandler = null; } } /** * Retrieves the handle of this ac * * @return */ public ActivityContextHandle getActivityContextHandle() { return activityContextHandle; } /** * Retrieve the {@link ActivityFlags} for this activity context * * @return */ public int getActivityFlags() { if (flags == null) { // instance has no flags stored, check local ac if (localActivityContext != null) { flags = localActivityContext.getActivityFlags(); } else { // local ac does not exists, get from cache flags = (Integer) cacheData .getObject(NODE_MAP_KEY_ACTIVITY_FLAGS); } } return flags; } /** * @return the factory */ public ActivityContextFactoryImpl getFactory() { return factory; } /** * test if the activity context is ending. * * @return true if ending. */ public boolean isEnding() { return cacheData.isEnding(); } /** * Set a shared data item for the ACI * * @param key * -- name of the shared data item. * @param newValue * -- value of the shared data item. */ public void setDataAttribute(String key, Object newValue) { cacheData.setCmpAttribute(key, newValue); if (logger.isDebugEnabled()) { logger.debug("Activity context with handle " + getActivityContextHandle() + " set cmp attribute named " + key + " to value " + newValue); } } /** * Get the shared data for the ACI. * * @param key * -- name we want to look up * @return the shared data for the ACI * */ public Object getDataAttribute(String key) { return cacheData.getCmpAttribute(key); } @SuppressWarnings("rawtypes") public Map getDataAttributes() { return cacheData.getCmpAttributesCopy(); } /** * add a naming binding to this activity context. * * @param aciName * - new name binding to be added. * */ public void addNameBinding(String aciName) { cacheData.nameBound(aciName); if (acReferencesHandler != null) { acReferencesHandler.nameReferenceCreated(); } } /** * This is called to release all the name bindings after the activity end * event is delivered to the sbb. * */ private void removeNamingBindings() { ActivityContextNamingFacility acf = sleeContainer .getActivityContextNamingFacility(); for (Object obj : cacheData.getNamesBoundCopy()) { String aciName = (String) obj; try { acf.removeName(aciName); } catch (Exception e) { logger.warn("failed to unbind name: " + aciName + " from ac:" + getActivityContextHandle(), e); } } } /** * Fetches set of names given to this ac * * @return Set containing String objects that are names of this ac */ @SuppressWarnings("unchecked") public Set<String> getNamingBindings() { return cacheData.getNamesBoundCopy(); } /** * Add the given name to the set of activity context names that we are bound * to. The AC Naming facility implicitly ends the activity after all names * are unbound. * * @param aciName * -- name to which we are bound. * @return true if name bind was removed; false otherwise * */ public boolean removeNameBinding(String aciName) { boolean removed = cacheData.nameUnbound(aciName); if (removed && acReferencesHandler != null) { acReferencesHandler.nameReferenceRemoved(); } return removed; } /** * attach the given timer to the current activity context. * * @param timerID * -- timer id to attach. * */ public boolean attachTimer(TimerID timerID) { if (cacheData.attachTimer(timerID)) { if (acReferencesHandler != null) { acReferencesHandler.timerReferenceCreated(); } return true; } else { return false; } } /** * Detach timer * * @param timerID * */ public boolean detachTimer(TimerID timerID) { boolean detached = cacheData.detachTimer(timerID); if (detached && acReferencesHandler != null) { acReferencesHandler.timerReferenceRemoved(); } return detached; } /** * Fetches set of attached timers. * * @return Set containing TimerID objects representing timers attached to * this ac. */ @SuppressWarnings("unchecked") public Set<TimerID> getAttachedTimers() { return cacheData.getAttachedTimers(); } // Spec Sec 7.3.4.1 Step 10. "The SLEE notifies the SLEE Facilities that // have references to the Activity Context that the Activ-ity // End Event has been delivered on the Activity Context. private void removeFromTimers() { TimerFacility timerFacility = sleeContainer.getTimerFacility(); // Iterate through the attached timers, telling the timer facility to // remove them for (Object obj : cacheData.getAttachedTimers()) { timerFacility.cancelTimer((TimerID) obj, false); } } /** * Mark this AC for garbage collection. It can no longer be used past this * point. * */ private void removeFromCache(TransactionContext txContext) { cacheData.remove(); } /** * attach an sbb entity to this AC. * * @param sbbEntityId * -- sbb entity to attach. * @return true if the SBB Entity is attached successfully, otherwise when * the SBB Entitiy has already been attached before, return false */ public boolean attachSbbEntity(SbbEntityID sbbEntityId) { boolean attached = cacheData.attachSbbEntity(sbbEntityId); if (attached) { if (acReferencesHandler != null) { acReferencesHandler.sbbeReferenceCreated(false); } } if (logger.isTraceEnabled()) { logger.trace("Attachement from sbb entity " + sbbEntityId + " to AC " + getActivityContextHandle() + " result: " + attached); } return attached; } /** * Detach the sbb entity * * @param sbbEntityId */ public void detachSbbEntity(SbbEntityID sbbEntityId) throws javax.slee.TransactionRequiredLocalException { boolean detached = cacheData.detachSbbEntity(sbbEntityId); if (detached && acReferencesHandler != null && !isEnding()) { acReferencesHandler.sbbeReferenceRemoved(); if (logger.isTraceEnabled()) { logger.trace("Detached sbb entity " + sbbEntityId + " from AC with handle " + getActivityContextHandle()); } } } /** * get an ordered copy of the set of SBBs attached to this ac. The ordering * is by SBB priority. * * @return list of SbbEIDs * */ public Set<SbbEntityID> getSortedSbbAttachmentSet( Set<SbbEntityID> excludeSet) { final Set<SbbEntityID> sbbAttachementSet = cacheData .getSbbEntitiesAttached(); Set<SbbEntityID> result = new HashSet<SbbEntityID>(); for (SbbEntityID sbbEntityId : sbbAttachementSet) { if (!excludeSet.contains(sbbEntityId)) { result.add(sbbEntityId); } } if (result.size() > 1) { result = sleeContainer.getSbbEntityFactory().sortByPriority(result); } return result; } public Set<SbbEntityID> getSbbAttachmentSet() { return cacheData.getSbbEntitiesAttached(); } /** * * @return */ public long getLastAccessTime() { final Long time = (Long) cacheData.getObject(NODE_MAP_KEY_LAST_ACCESS); return time == null ? System.currentTimeMillis() : time.longValue(); } // --- private helpers private void updateLastAccessTime(boolean creation) { if (creation) { cacheData.putObject(NODE_MAP_KEY_LAST_ACCESS, Long.valueOf(System.currentTimeMillis())); } else { ActivityManagementConfiguration configuration = factory .getConfiguration(); Long lastUpdate = (Long) cacheData .getObject(NODE_MAP_KEY_LAST_ACCESS); if (lastUpdate != null) { final long now = System.currentTimeMillis(); if ((now - configuration.getMinTimeBetweenUpdatesInMs()) > lastUpdate .longValue()) { // last update if (logger.isTraceEnabled()) { logger.trace("Updating access time for AC with handle " + getActivityContextHandle()); } cacheData.putObject(NODE_MAP_KEY_LAST_ACCESS, Long.valueOf(now)); } else { if (logger.isDebugEnabled()) { logger.debug("Skipping update of access time for AC with handle " + getActivityContextHandle()); } } } else { cacheData.putObject(NODE_MAP_KEY_LAST_ACCESS, Long.valueOf(System.currentTimeMillis())); if (logger.isTraceEnabled()) { logger.trace("Updating access time for AC with handle " + getActivityContextHandle()); } } } } public String toString() { return new StringBuilder("ActivityContext{ handle = ") .append(activityContextHandle).append(" }").toString(); } // emmartins: added to split null activity end related logic public boolean isSbbAttachmentSetEmpty() { return cacheData.noSbbEntitiesAttached(); } public boolean isAttachedTimersEmpty() { return cacheData.noTimersAttached(); } public boolean isNamingBindingEmpty() { return cacheData.noNamesBound(); } public boolean equals(Object obj) { if (obj != null && obj.getClass() == this.getClass()) { return ((ActivityContextImpl) obj).activityContextHandle .equals(this.activityContextHandle); } else { return false; } } public int hashCode() { return activityContextHandle.hashCode(); } /* * (non-Javadoc) * * @see * org.mobicents.slee.container.activity.ActivityContext#fireEvent(javax * .slee.EventTypeID, java.lang.Object, javax.slee.Address, * javax.slee.ServiceID, * org.mobicents.slee.container.event.EventProcessingSucceedCallback, * org.mobicents.slee.container.event.EventProcessingFailedCallback, * org.mobicents.slee.container.event.EventUnreferencedCallback) */ public void fireEvent(EventTypeID eventTypeId, Object event, Address address, ServiceID serviceID, EventProcessingSucceedCallback succeedCallback, EventProcessingFailedCallback failedCallback, EventUnreferencedCallback unreferencedCallback) throws ActivityIsEndingException, SLEEException { if (isEnding()) { throw new ActivityIsEndingException(getActivityContextHandle() .toString()); } if (acReferencesHandler != null) { acReferencesHandler.eventReferenceCreated(); } fireEvent( sleeContainer.getEventContextFactory().createEventContext( eventTypeId, event, this, address, serviceID, succeedCallback, failedCallback, unreferencedCallback), sleeContainer.getTransactionManager().getTransactionContext()); } public void fireEvent(EventTypeID eventTypeId, Object event, Address address, ServiceID serviceID, EventContext reference) throws ActivityIsEndingException, SLEEException { if (isEnding()) { throw new ActivityIsEndingException(getActivityContextHandle() .toString()); } if (acReferencesHandler != null) { acReferencesHandler.eventReferenceCreated(); } fireEvent( sleeContainer.getEventContextFactory().createEventContext( eventTypeId, event, this, address, serviceID, reference), sleeContainer .getTransactionManager().getTransactionContext()); } /** * Ends the activity context. */ public void endActivity() { if (logger.isDebugEnabled()) { logger.debug("Ending activity context with handle " + getActivityContextHandle()); } if (cacheData.setEnding(true)) { fireEvent( sleeContainer .getEventContextFactory() .createActivityEndEventContext( this, new ActivityEndEventUnreferencedCallback( getActivityContextHandle(), factory)), sleeContainer.getTransactionManager() .getTransactionContext()); } } public void activityEnded() { // remove references to this AC in timer and ac naming facility removeNamingBindings(); removeFromTimers(); // Spec 7.3.4.1 Step 10 final int activityFlags = activityContextHandle.getActivityType() == ActivityType.RA ? getActivityFlags() : -1; TransactionContext txContext = sleeContainer.getTransactionManager() .getTransactionContext(); removeFromCache(txContext); factory.removeActivityContext(this); if (activityContextHandle.getActivityType() == ActivityType.RA) { // external activity, notify RA that the activity has ended ((ResourceAdaptorActivityContextHandle) activityContextHandle) .getResourceAdaptorEntity().activityEnded( activityContextHandle.getActivityHandle(), activityFlags); } else if (activityContextHandle.getActivityType() == ActivityType.SERVICE) { sleeContainer.getServiceManagement().activityEnded( (ServiceActivityHandle) activityContextHandle .getActivityHandle()); } } private void fireEvent(EventContext event, TransactionContext txContext) { if (logger.isDebugEnabled()) { logger.debug("Firing " + event); } final ActivityEventQueueManager aeqm = event.getLocalActivityContext() .getEventQueueManager(); if (aeqm != null) { if (txContext != null) { // put event as pending in ac event queue manager aeqm.pending(event); // add tx actions to commit or rollback txContext.getAfterCommitPriorityActions().add( new CommitEventContextAction(event, aeqm)); txContext.getAfterRollbackActions().add( new RollbackEventContextAction(event, aeqm)); } else { // commit event, there is no tx aeqm.fireNotTransacted(event); } } else { throw new SLEEException("unable to find ACs event queue manager"); } } private LocalActivityContextImpl localActivityContext; /* * (non-Javadoc) * * @see * org.mobicents.slee.runtime.activity.ActivityContext#getLocalActivityContext * () */ public LocalActivityContextImpl getLocalActivityContext() { if (localActivityContext == null) { localActivityContext = factory.getLocalActivityContext(this); } return localActivityContext; } public void activityUnreferenced() { if (logger.isDebugEnabled()) { logger.debug("Activity Context with handle " + activityContextHandle + " is now unreferenced"); } switch (activityContextHandle.getActivityType()) { case RA: // external activity, notify RA that the activity is unreferenced ((ResourceAdaptorActivityContextHandle) activityContextHandle) .getResourceAdaptorEntity() .getResourceAdaptorObject() .activityUnreferenced( activityContextHandle.getActivityHandle()); break; case NULL: // null activity unreferenced, end it ActivityContext ac = sleeContainer.getActivityContextFactory() .getActivityContext(activityContextHandle); if (ac != null) ac.endActivity(); break; default: // do nothing break; } } /* * (non-Javadoc) * * @see org.mobicents.slee.runtime.activity.ActivityContext# * getActivityContextInterface() */ public ActivityContextInterfaceImpl getActivityContextInterface() { return new ActivityContextInterfaceImpl(this); } public ActivityContextReferencesHandler getAcReferencesHandler() { return acReferencesHandler; } @Override public void beforeDeliveringEvent(EventContext ec) { if (acReferencesHandler != null) { // this means the ac is attached, thus we can indicate there a sbb // ref // to the refs handler, which means that more detachs must exist // than // attachs, for a unref state must be possible // BUT only if there is no sbb refs yet, cause otherwise it means // there was an // attachment before delivering the event, yep the sbb entity // receiving the event // the flag is to signal if this is special sbb reference due to // event delivery acReferencesHandler.sbbeReferenceCreated(true); } } @Override public String getStringID() { return getStringID(true); } public String getStringID(boolean createIfNull) { String sid = cacheData.getStringID(); if (sid == null && createIfNull) { sid = sleeContainer.getUuidGenerator().createUUID(); cacheData.setStringID(sid); } return sid; } }