package org.mobicents.slee.container.profile; import java.security.AccessController; import java.security.PrivilegedAction; import javax.slee.CreateException; import javax.slee.SLEEException; import javax.slee.profile.ProfileLocalObject; import javax.slee.profile.ProfileVerificationException; import javax.slee.profile.UnrecognizedProfileNameException; import javax.slee.resource.EventFlags; import javax.transaction.SystemException; import org.apache.log4j.Logger; import org.mobicents.slee.container.SleeContainerUtils; import org.mobicents.slee.container.component.ProfileSpecificationComponent; import org.mobicents.slee.container.component.profile.ProfileConcreteClassInfo; import org.mobicents.slee.container.component.profile.ProfileEntity; import org.mobicents.slee.container.component.profile.ProfileEntityFactory; import org.mobicents.slee.container.component.profile.ProfileEntityFramework; import org.mobicents.slee.runtime.activity.ActivityContext; import org.mobicents.slee.runtime.facilities.profile.AbstractProfileEvent; import org.mobicents.slee.runtime.facilities.profile.ProfileAddedEventImpl; import org.mobicents.slee.runtime.facilities.profile.ProfileRemovedEventImpl; import org.mobicents.slee.runtime.facilities.profile.ProfileUpdatedEventImpl; import org.mobicents.slee.runtime.transaction.SleeTransactionManager; import org.mobicents.slee.runtime.transaction.TransactionalAction; /** * Start time:16:46:52 2009-03-13<br> * Project: mobicents-jainslee-server-core<br> * * Class representing Profile Object - this object servers as place holder for * selected profiles. ProfileObject can belong to only one profile table during * its lifecycle, ever. * * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a> * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a> * @author martins */ public class ProfileObject { /** * */ private static final Logger logger = Logger.getLogger(ProfileObject.class); /** * the state of the profile object */ private ProfileObjectState state = ProfileObjectState.DOES_NOT_EXIST; /** * the profile table the profile is related */ private final ProfileTableImpl profileTable; /** * the instance of the concrete implementation of the profile spec */ private final ProfileConcrete profileConcrete; /** * a snapshot copy of the current profile pojo, before updates, needed * for profile table events */ private ProfileEntity profileEntitySnapshot; /** * the context of the profile object */ private ProfileContextImpl profileContext = null; /** * inidcates if this profile can be invoked more than once in a call tree for a single tx, thus exposed to loops */ private final boolean profileReentrant; /** * indicates if the object is related with a slee 1.1 profile spec or not */ private final boolean isSlee11; /** * the entity holding cmp data of currently assigned profile */ private ProfileEntity profileEntity; /** * */ private final boolean readOnlyProfileTable; /** * */ private final ProfileEntityFramework profileEntityFramework; /** * */ private boolean persisted; /** * used to filter invocations to profile concrete */ private final ProfileConcreteClassInfo profileConcreteClassInfo; /** * * @param profileTable * @param profileSpecificationId * @throws NullPointerException * @throws SLEEException */ public ProfileObject(ProfileTableImpl profileTable) throws NullPointerException, SLEEException { if (profileTable == null) { throw new NullPointerException("Parameters must not be null"); } this.profileTable = profileTable; final ProfileSpecificationComponent component = profileTable.getProfileSpecificationComponent(); this.profileReentrant = component.getDescriptor().getProfileAbstractClass() == null ? false : this.profileTable.getProfileSpecificationComponent().getDescriptor().getProfileAbstractClass().getReentrant(); this.readOnlyProfileTable = component.getDescriptor().getReadOnly(); this.profileConcreteClassInfo = component.getProfileConcreteClassInfo(); this.isSlee11 = profileTable.getProfileSpecificationComponent().isSlee11(); try { this.profileConcrete = (ProfileConcrete) component.getProfileCmpConcreteClass().newInstance(); this.profileConcrete.setProfileObject(this); } catch (Throwable e) { throw new SLEEException(e.getMessage(), e); } this.profileEntityFramework = component.getProfileEntityFramework(); } /** * Invoked from pool. * * @param profileContext */ public void setProfileContext(ProfileContextImpl profileContext) { if(logger.isDebugEnabled()) { logger.debug("[setProfileContext] "+this); } if (profileContext == null) { throw new NullPointerException("Passed context must not be null."); } if (state != ProfileObjectState.DOES_NOT_EXIST) { throw new IllegalStateException("Wrong state: " + this.state + ",on profile set context operation, for profile table: " + this.profileTable.getProfileTableName() + " with specification: " + this.profileTable.getProfileSpecificationComponent().getProfileSpecificationID()); } this.profileContext = profileContext; this.profileContext.setProfileObject(this); if (profileConcreteClassInfo.isInvokeSetProfileContext()) { final ClassLoader oldClassLoader = SleeContainerUtils.getCurrentThreadClassLoader(); try { final ClassLoader cl = this.profileTable.getProfileSpecificationComponent().getClassLoader(); if (System.getSecurityManager()!=null) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { Thread.currentThread().setContextClassLoader(cl); return null; } }); } else { Thread.currentThread().setContextClassLoader(cl); } try { if (isSlee11) { try { profileConcrete.setProfileContext(profileContext); } catch (RuntimeException e) { runtimeExceptionOnProfileInvocation(e); } } } catch (Exception e) { if (logger.isDebugEnabled()) logger.debug("Exception encountered while setting profile context for profile table: " + this.profileTable.getProfileTableName() + " with specification: " + this.profileTable.getProfileSpecificationComponent().getProfileSpecificationID(), e); } } finally { if (System.getSecurityManager()!=null) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { Thread.currentThread().setContextClassLoader(oldClassLoader); return null; } }); } else { Thread.currentThread().setContextClassLoader(oldClassLoader); } } } state = ProfileObjectState.POOLED; } /** * Creation of the profile with the specified name * @param profileName * @throws CreateException */ public void profileCreate(String profileName) throws CreateException { profileInitialize(profileName); profilePostCreate(); profileStore(); profilePersist(); } public void profilePersist() { persistProfileConcrete(); persisted = true; } /** * initialize state from default profile */ private void profileInitialize(String profileName) { if(logger.isDebugEnabled()) { logger.debug("[profileInitialize] "+this+" , profileName = "+profileName); } if (this.state != ProfileObjectState.POOLED) { throw new SLEEException(this.toString()); } if (profileName == null) { // default profile creation // create instance of entity profileEntity = profileEntityFramework.getProfileEntityFactory().newInstance(profileTable.getProfileTableName(), null); // change state this.state = ProfileObjectState.PROFILE_INITIALIZATION; // invoke life cycle method on profile if (profileConcreteClassInfo.isInvokeProfileInitialize()) { try { profileConcrete.profileInitialize(); } catch (RuntimeException e) { runtimeExceptionOnProfileInvocation(e); } } } else { // load the default profile entity if (logger.isDebugEnabled()) { logger.debug("Copying state from default profile on object "+this); } profileEntity = cloneEntity(profileTable.getDefaultProfileEntity()); profileEntity.setProfileName(profileName); } // mark entity as dirty and for creation profileEntity.create(); profileEntity.setDirty(true); } /** * * @throws CreateException */ private void profilePostCreate() throws CreateException { if(logger.isDebugEnabled()) { logger.debug("[profilePostCreate] "+this); } if (this.state != ProfileObjectState.POOLED && this.state != ProfileObjectState.PROFILE_INITIALIZATION) { throw new SLEEException(this.toString()); } this.state = ProfileObjectState.READY; if (isSlee11) { if (profileConcreteClassInfo.isInvokeProfilePostCreate()) { try { this.profileConcrete.profilePostCreate(); } catch (RuntimeException e) { runtimeExceptionOnProfileInvocation(e); } } } } /** * * Activates the profile object and use a specific snapshot of a profile entity * @param snapshot */ public void profileActivate(ProfileEntity profileEntity) { profileActivate(); profileLoad(profileEntity); } /** * Activates the profile object and loads its persistent state * @param profileName * @throws UnrecognizedProfileNameException */ public void profileActivate(String profileName) throws UnrecognizedProfileNameException { profileActivate(); profileLoad(profileName); } /** * */ private void profileActivate() { if(logger.isDebugEnabled()) { logger.debug("[profileActivate] "+this); } if (state != ProfileObjectState.POOLED) { throw new SLEEException(this.toString()); } if (isSlee11) { if (profileConcreteClassInfo.isInvokeProfileActivate()) { try { profileConcrete.profileActivate(); } catch (RuntimeException e) { runtimeExceptionOnProfileInvocation(e); } } } this.state = ProfileObjectState.READY; } /** * */ private void profileLoad(String profileName) throws UnrecognizedProfileNameException { if(logger.isDebugEnabled()) { logger.debug("[profileLoad] "+this+" , profileName = "+profileName); } ProfileEntity profileEntity = loadProfileEntity(profileName); profileEntity.setReadOnly(readOnlyProfileTable); profileLoad(profileEntity); } /** * * @param profileEntity */ private void profileLoad(ProfileEntity profileEntity) { if(logger.isDebugEnabled()) { logger.debug("[profileLoad] "+this+" , profileEntity = "+profileEntity); } if (state != ProfileObjectState.READY) { throw new SLEEException(this.toString()); } this.profileEntity = profileEntity; // create a snapshot copy if the profile table fires events if (profileTable.doesFireEvents()) { profileEntitySnapshot = cloneEntity(profileEntity); profileEntitySnapshot.setReadOnly(true);; } if (profileConcreteClassInfo.isInvokeProfileLoad()) { try { this.profileConcrete.profileLoad(); } catch (RuntimeException e) { runtimeExceptionOnProfileInvocation(e); } } } /** * * @throws ProfileVerificationException */ public void profileVerify() throws ProfileVerificationException { if(logger.isDebugEnabled()) { logger.debug("[profileVerify] "+this); } profileStore(); if (state != ProfileObjectState.READY) { throw new SLEEException(this.toString()); } if(!isSlee11 || profileEntity.getProfileName() != null) { // only invoke when it is a slee 1.0 profile or a non default slee 1.1 profile if (profileConcreteClassInfo.isInvokeProfileVerify()) { try { profileConcrete.profileVerify(); } catch (RuntimeException e) { runtimeExceptionOnProfileInvocation(e); } } } } private void runtimeExceptionOnProfileInvocation(RuntimeException e) { logger.error("Runtime exception when invoking concrete profile! Setting tx for rollback and invalidating object",e); final SleeTransactionManager txManager = profileTable.getSleeContainer().getTransactionManager(); try { if (txManager.getTransaction() != null) { txManager.setRollbackOnly(); } } catch (Throwable f) { logger.error(f.getMessage(),f); } invalidateObject(); throw e; } /** * */ public void profileStore() { if(logger.isDebugEnabled()) { logger.debug("[profileStore] "+this); } if (state != ProfileObjectState.READY) { throw new SLEEException(this.toString()); } if (profileEntity.isReadOnly()) { return; } if (profileConcreteClassInfo.isInvokeProfileStore()) { try { profileConcrete.profileStore(); } catch (RuntimeException e) { runtimeExceptionOnProfileInvocation(e); } } } /** * */ public void profilePassivate() { if (state == ProfileObjectState.READY) { // FIXME tests/profiles/lifecycle/Test1110227Test.xml enforces profileStore to be called before every profilePassivate(), doing this here to enfore it happens profileStore(); if(logger.isDebugEnabled()) { logger.debug("[profilePassivate] "+this); } if (this.profileEntity.getProfileName() != null) { state = ProfileObjectState.POOLED; } else { // FIXME due to tests/profiles/profileabstractclass/Test1110251Test.xml we need to get ridden of the default profile object state = ProfileObjectState.DOES_NOT_EXIST; } if (profileConcreteClassInfo.isInvokeProfilePassivate()) { if (isSlee11) { try { profileConcrete.profilePassivate(); } catch (RuntimeException e) { runtimeExceptionOnProfileInvocation(e); } } } } this.profileEntity = null; this.profileEntitySnapshot = null; } /** * */ public void profileRemove(boolean invokeConcreteSbb) { if(logger.isDebugEnabled()) { logger.debug("[profileRemove] "+this); } if (state != ProfileObjectState.READY) { throw new SLEEException(this.toString()); } if (isSlee11 && invokeConcreteSbb) { if (profileConcreteClassInfo.isInvokeProfileRemove()) { try { profileConcrete.profileRemove(); } catch (RuntimeException e) { runtimeExceptionOnProfileInvocation(e); } } } if (profileTable.doesFireEvents() && profileEntity.getProfileName() != null && profileEntitySnapshot != null) { // fire event AbstractProfileEvent event = new ProfileRemovedEventImpl(profileEntity); if (logger.isDebugEnabled()) { logger.debug("firing profile removed event for profile named "+profileEntity); } profileTable.getActivityContext().fireEvent(event.getEventTypeID(), event, event.getProfileAddress(), null, EventFlags.NO_FLAGS); } if(profileEntity.getProfileName() != null) { profileEntityFramework.removeprofile(profileEntity); } else { // the default profile entity is not stored in the framework profileTable.setDefaultProfileEntity(null); } state = ProfileObjectState.POOLED; this.profileEntity = null; this.profileEntitySnapshot = null; } /** * Invoked when pool removes object */ public void unsetProfileContext() { if(logger.isDebugEnabled()) { logger.debug("[unsetProfileContext] "+this); } if (state == ProfileObjectState.POOLED && profileConcreteClassInfo.isInvokeUnsetProfileContext()) { final ClassLoader oldClassLoader = SleeContainerUtils.getCurrentThreadClassLoader(); try { final ClassLoader cl = profileTable.getProfileSpecificationComponent().getClassLoader(); if (System.getSecurityManager()!=null) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { Thread.currentThread().setContextClassLoader(cl); return null; } }); } else { Thread.currentThread().setContextClassLoader(cl); } if (isSlee11) { try { profileConcrete.unsetProfileContext(); } catch (RuntimeException e) { runtimeExceptionOnProfileInvocation(e); } } profileContext.setProfileObject(null); state = ProfileObjectState.DOES_NOT_EXIST; } finally { if (System.getSecurityManager()!=null) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { Thread.currentThread().setContextClassLoader(oldClassLoader); return null; } }); } else { Thread.currentThread().setContextClassLoader(oldClassLoader); } } } } // ------------ /** * */ public ProfileConcrete getProfileConcrete() { return profileConcrete; } /** * */ public ProfileEntity getProfileEntity() { return profileEntity; } /** * Retrieves the local representation for this profile object * @return */ public ProfileLocalObject getProfileLocalObject() { final Class<?> profileLocalObjectConcreteClass = profileTable.getProfileSpecificationComponent().getProfileLocalObjectConcreteClass(); ProfileLocalObject profileLocalObject = null; if (profileLocalObjectConcreteClass == null) { profileLocalObject = new ProfileLocalObjectImpl(this); } else { try { profileLocalObject = (ProfileLocalObject) profileLocalObjectConcreteClass.getConstructor(ProfileObject.class).newInstance(this); } catch (Throwable e) { throw new SLEEException(e.getMessage(),e); } } return profileLocalObject; } /** * * @return */ public ProfileObjectState getState() { return state; } /** * * @return */ public boolean isProfileReentrant() { return profileReentrant; } /** * * @return */ public ProfileTableImpl getProfileTable() { return profileTable; } private ProfileEntity cloneEntity(final ProfileEntity source) { final ProfileEntityFactory profileEntityFactory = profileTable.getProfileSpecificationComponent().getProfileEntityFramework().getProfileEntityFactory(); final ProfileEntity result = profileEntityFactory.newInstance(source.getTableName(), source.getProfileName()); if(System.getSecurityManager()==null) { profileEntityFactory.copyAttributes(source, result); }else { AccessController.doPrivileged(new PrivilegedAction(){ public Object run() { profileEntityFactory.copyAttributes(source, result); return null; }}); } return result; } /** * */ private ProfileEntity loadProfileEntity(String profileName) throws UnrecognizedProfileNameException { if (profileName != null) { ProfileEntity profileEntity = profileEntityFramework.retrieveProfile(profileTable.getProfileTableName(), profileName); if (profileEntity == null) { throw new UnrecognizedProfileNameException(); } return profileEntity; } else { // clone default profile entity final ProfileEntity profileEntity = cloneEntity(profileTable.getDefaultProfileEntity()); // add a tx action after commit to update the state on the one stored in the table TransactionalAction action = new TransactionalAction() { public void execute() { profileTable.setDefaultProfileEntity(cloneEntity(profileEntity)); } }; try { profileTable.getSleeContainer().getTransactionManager().addAfterCommitAction(action); } catch (SystemException e) { throw new SLEEException(e.getMessage(), e); } return profileEntity; } } /** * */ private void persistProfileConcrete() { if (profileEntity.isCreate()) { if (logger.isDebugEnabled()) { logger.debug("Persisting "+this); } if (profileEntity.getProfileName() != null) { // the default profile entity is not persisted using the framework profileEntityFramework.persistProfile(profileEntity); } else { profileTable.setDefaultProfileEntity(cloneEntity(profileEntity)); } } } public String toString() { return "ProfileObject( table= "+profileTable.getProfileTableName()+" , state = "+state+" , entity = "+ profileEntity+" )"; } /** * use this method to move the object to {@link ProfileObjectState#DOES_NOT_EXIST} state and indicate to the pool that the object should be discarded */ public void invalidateObject() { state = ProfileObjectState.DOES_NOT_EXIST; } /** * Fires a profile added or updated event if the profile object state is ready and the persistent state is dirty */ public void fireAddOrUpdatedEventIfNeeded() { if (state == ProfileObjectState.READY) { if (profileEntity.isDirty()) { // check the table fires events and the object is not assigned to a default profile if (profileTable.doesFireEvents() && profileEntity.getProfileName() != null) { // Fire a Profile Added or Updated Event ActivityContext ac = profileTable.getActivityContext(); AbstractProfileEvent event = null; if (profileEntity.isCreate()) { if (persisted) { event = new ProfileAddedEventImpl(profileEntity); persisted = false; if (logger.isDebugEnabled()) { logger.debug("firing profile added event for profile named "+profileEntity); } } else { return; } } else { event = new ProfileUpdatedEventImpl(profileEntitySnapshot,profileEntity); if (logger.isDebugEnabled()) { logger.debug("firing profile updated event for profile named "+profileEntity); } } ac.fireEvent(event.getEventTypeID(), event, event.getProfileAddress(), null, EventFlags.NO_FLAGS); } } } } /** * the profile cmp slee 1.0 wrapper for this profile object */ private AbstractProfileCmpSlee10Wrapper profileCmpSlee10Wrapper; /** * Retrieves the profile cmp slee 1.0 wrapper for this profile object * @return */ public AbstractProfileCmpSlee10Wrapper getProfileCmpSlee10Wrapper() { if (profileCmpSlee10Wrapper == null) { try { profileCmpSlee10Wrapper = (AbstractProfileCmpSlee10Wrapper) profileTable.getProfileSpecificationComponent().getProfileCmpSlee10WrapperClass().getConstructor(ProfileObject.class).newInstance(this); } catch (Throwable e) { throw new SLEEException(e.getMessage(),e); } } return profileCmpSlee10Wrapper; } }