package org.mobicents.slee.container.profile; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import javax.management.StandardMBean; import javax.persistence.PersistenceException; import javax.slee.InvalidStateException; import javax.slee.SLEEException; import javax.slee.management.ManagementException; import javax.slee.profile.ProfileImplementationException; import javax.slee.profile.ProfileMBean; import javax.slee.profile.ProfileVerificationException; import javax.slee.profile.ReadOnlyProfileException; import javax.transaction.RollbackException; import javax.transaction.Transaction; import org.apache.log4j.Logger; import org.hibernate.exception.ConstraintViolationException; import org.mobicents.slee.container.SleeContainer; import org.mobicents.slee.container.component.profile.ProfileEntity; import org.mobicents.slee.container.security.Utility; import org.mobicents.slee.runtime.transaction.SleeTransactionManager; /** * Start time:12:23:15 2009-03-18<br> * Project: mobicents-jainslee-server-core<br> * * This is stub class, that is extended and instrumented to allow access to * underlying Profile * * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a> * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a> * @author martins */ public abstract class AbstractProfileMBeanImpl extends StandardMBean implements AbstractProfileMBean { private enum State { read, write }; private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(AbstractProfileMBeanImpl.class); protected final static SleeContainer sleeContainer = SleeContainer.lookupFromJndi(); /** * the table related with the profile mbean */ private final ProfileTableImpl profileTable; /** * the name of the profile assigned to the mbean */ private final String profileName; /** * current state in the mbean */ private State state = State.read; /** * the transaction curretly assigned to the mbean, if any */ private Transaction transaction; /** * * @param mbeanInterface * @param profileObject * @throws NotCompliantMBeanException */ public AbstractProfileMBeanImpl(Class<?> mbeanInterface, String profileName, ProfileTableImpl profileTable) throws NotCompliantMBeanException, ManagementException { super(mbeanInterface); this.profileTable = profileTable; this.profileName = profileName; } /** * Retrieves the profile object currently bound to the mbean transaction * @return */ protected ProfileObject getProfileObject() { return profileTable.getProfile(profileName); } /** * Registers the mbean in the server * @throws ManagementException */ public void register() throws ManagementException { // try { // sleeContainer.getMBeanServer().registerMBean(this, getObjectName()); // } catch (Throwable e) { // throw new ManagementException(e.getMessage(),e); // } Utility.registerSafelyMBean(sleeContainer, getObjectName(), this); } /** * Unregisters the mbean in the server * @throws ManagementException */ public void unregister() throws ManagementException { // try { // sleeContainer.getMBeanServer().unregisterMBean(getObjectName()); // } catch (Throwable e) { // throw new ManagementException(e.getMessage(),e); // } Utility.unregisterSafelyMBean(sleeContainer, getObjectName()); } public void close() throws ManagementException { try { // close mbean if (state == State.write) { restoreProfile(); } closeProfile(); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } /** * Closes and unregisters the mbean for the specified profile, if exists * @param profileTableName * @param profileName */ public static void close(String profileTableName, String profileName) { final ObjectName objectName = getObjectName(profileTableName, profileName); if (sleeContainer.getMBeanServer().isRegistered(objectName)) { Runnable r = new Runnable() { public void run() { try { sleeContainer.getMBeanServer().invoke(objectName, "close", new Object[] {}, new String[] {}); } catch (Throwable e) { logger.error(e.getMessage(),e); } } }; Thread t = new Thread(r); t.start(); } } /** * Retrieves the object name used to register this mbean. * @return */ public ObjectName getObjectName() { return getObjectName(profileTable.getProfileTableName(), profileName); } /** * * Retrieves the JMX ObjectName for a profile, given its profile name and * profile table name * * @param profileTableName * @param profileName * @return * @throws MalformedObjectNameException */ public static ObjectName getObjectName( String profileTableName, String profileName) { try { return new ObjectName(ProfileMBean.BASE_OBJECT_NAME + ',' + ProfileMBean.PROFILE_TABLE_NAME_KEY + '=' + ObjectName.quote(profileTableName) + ',' + ProfileMBean.PROFILE_NAME_KEY + '=' + ObjectName.quote(profileName != null ? profileName : "") //+ ",uuid=" + ObjectName.quote(UUID.randomUUID().toString())); // specially for tck ); } catch (Throwable e) { throw new SLEEException(e.getMessage(),e); } } /** * Retrieves the profile name * @return */ protected String getProfileName() { return profileName; } /** * Retreives the profile tbale name * @return */ protected String getProfileTableName() { return profileTable.getProfileTableName(); } /** * Moves to the write mode, using specified object. The current java transaction will be suspended. * @throws ManagementException */ private void writeMode() throws SLEEException, ManagementException { if (!isProfileWriteable()) { if(logger.isDebugEnabled()) { logger.debug("Changing state to read-write, for profile mbean with name "+profileName+", from table with name "+this.profileTable.getProfileTableName()); } // get object & make it writable ProfileObject profileObject = profileTable.getProfile(profileName); profileObject.getProfileEntity().setReadOnly(false); // change state state = State.write; } else { if(logger.isDebugEnabled()) { logger.debug("Already in write state, for profile mbean with name "+profileName+", from table with name "+this.profileTable.getProfileTableName()); } } } /** * */ private void readMode() { if(logger.isDebugEnabled()) { logger.debug("Changing state to read-only, for profile mbean with name "+profileName+", from table with name "+this.profileTable.getProfileTableName()); } state = State.read; } // ################# // # MBean methods # // ################# /* * (non-Javadoc) * @see javax.slee.profile.ProfileMBean#closeProfile() */ public void closeProfile() throws InvalidStateException, ManagementException { if (logger.isDebugEnabled()) { logger.debug("closeProfile() on: " + profileName + ", from table:" +profileTable.getProfileTableName()); } // The closeProfile method must throw a javax.slee.InvalidStateException if the Profile MBean object is in the read-write state. if (this.isProfileWriteable()) throw new InvalidStateException(); // unregister mbean unregister(); } /* * (non-Javadoc) * @see javax.slee.profile.ProfileMBean#commitProfile() * * This method commits profile. See descritpion - profile becomes visible for slee ONLY when its commited. */ public void commitProfile() throws InvalidStateException, ProfileVerificationException, ManagementException { if (logger.isDebugEnabled()) { logger.debug("commitProfile() on: "+ profileName + ", from table:" +profileTable.getProfileTableName()); } if (!this.isProfileWriteable()) throw new InvalidStateException("not in write state"); final SleeTransactionManager txManager = sleeContainer.getTransactionManager(); ProfileEntity profileEntity = null; try { // resume tx txManager.resume(this.transaction); // verify state ProfileObject profileObject = getProfileObject(); profileObject.profileVerify(); // check if the tx is marked for rollback if (txManager.getRollbackOnly()) { // FIXME can't undertsand why the rollback and change of state, perhaps tests/profiles/lifecycle/Test1110227Test.xml is faulty?? txManager.rollback(); readMode(); this.transaction = null; throw new RollbackException("the tx is marked for rollback, can't proceeed with commit"); } // "save" profile entity, we may need to recover its current state profileEntity = profileObject.getProfileEntity(); // commit tx this.transaction = null; txManager.commit(); // change mode readMode(); } catch (ProfileVerificationException e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(),e); } throw e; } catch (RollbackException e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(),e); } if (e.getCause() instanceof PersistenceException && e.getCause().getCause() instanceof ConstraintViolationException) { throw new ProfileVerificationException(e.getCause().getMessage(),e); } else if (e.getCause() != null && e.getCause().getClass() == Throwable.class && e.getCause().getMessage() != null && e.getCause().getMessage().equals("setRollbackOnly called from:")) { // workaround for issue with jboss AS 5.1.0 GA / Hibernate Entity Manager 3.4.0.GA / JBoss TS 4.6.1.GA, the persistentexception is not thrown throw new ProfileVerificationException(e.getCause().getMessage(),e); } else { throw new ManagementException(e.getMessage(),e); } } catch (Throwable e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(),e); } throw new ManagementException(e.getMessage(),e); } finally { if(this.transaction == null) { if (isProfileWriteable()) { // still in write mode if (logger.isDebugEnabled()) { logger.debug("The tx commit failed, recreating tx with current profile entity state"); } try { txManager.begin(); this.transaction = txManager.getTransaction(); ProfileEntity newTxProfileEntity = profileEntity.isCreate() ? profileTable.createProfile(profileName).getProfileEntity() : getProfileObject().getProfileEntity(); profileTable.getProfileSpecificationComponent().getProfileEntityFramework().getProfileEntityFactory().copyAttributes(profileEntity, newTxProfileEntity); newTxProfileEntity.setReadOnly(false); txManager.suspend(); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } } else { // tx still valid try { txManager.suspend(); } catch (Throwable f) { logger.error(f.getMessage(),f); } } } } public void createProfile() throws ManagementException { if(logger.isDebugEnabled()) { logger.debug("Creating profile with name "+profileName+", from table with name "+this.profileTable.getProfileTableName()); } if (!isProfileWriteable()) { final SleeTransactionManager txManager = sleeContainer.getTransactionManager(); try { writeMode(); this.transaction = txManager.suspend(); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } } public void editProfile() throws ManagementException { if(logger.isDebugEnabled()) { logger.debug("Editing profile with name "+profileName+", from table with name "+this.profileTable.getProfileTableName()); } if (!isProfileWriteable()) { final SleeTransactionManager txManager = sleeContainer.getTransactionManager(); try { txManager.begin(); writeMode(); this.transaction = txManager.suspend(); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } } public boolean isProfileDirty() throws ManagementException { if(logger.isDebugEnabled()) { logger.debug("isProfileDirty() on: "+profileName+", from table:"+profileTable.getProfileTableName()); } /* * The isProfileDirty method returns true if the Profile MBean object is * in the read-write state and the dirty flag of the Profile Management * object that caches the persistent state of the Profile returns true. * This method returns false under any other situation. */ if (isProfileWriteable()) { final SleeTransactionManager txManager = sleeContainer.getTransactionManager(); try { txManager.resume(this.transaction); return getProfileObject().getProfileEntity().isDirty(); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } finally { try { txManager.suspend(); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } } else { return false; } } public boolean isProfileWriteable() throws ManagementException { return state == State.write; } public void restoreProfile() throws InvalidStateException, ManagementException { if (logger.isDebugEnabled()) { logger.debug("restoreProfile() on: "+profileName+", from table:"+ profileTable.getProfileTableName()); } if (!isProfileWriteable()) { throw new InvalidStateException("The restoreProfile method must throw a javax.slee.InvalidStateException if the Profile MBean object is not in the read-write state."); } final SleeTransactionManager txManager = sleeContainer.getTransactionManager(); try { txManager.resume(transaction); txManager.rollback(); transaction = null; readMode(); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } // cmp accessor helpers /** * Logic to execute before invoking a cmp setter method on the mbean * @return true if the method resumed a transaction * @throws ManagementException */ protected void beforeSetCmpField() throws ManagementException, InvalidStateException { if (logger.isDebugEnabled()) { logger.debug("beforeSetCmpField() on profile with name "+profileName+" of table "+profileTable.getProfileTableName()); } if (isProfileWriteable()) { try { sleeContainer.getTransactionManager().resume(transaction); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } else { throw new InvalidStateException(); } } /** * Logic to execute after invoking a cmp setter method on the mbean * @throws ManagementException */ protected void afterSetCmpField() throws ManagementException { if (logger.isDebugEnabled()) { logger.debug("afterSetCmpField() on profile with name "+profileName+" of table "+profileTable.getProfileTableName()); } try { sleeContainer.getTransactionManager().suspend(); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } /** * Logic to execute before invoking a cmp getter method on the mbean * @return true if the method initiated or resumed a transaction * @throws ManagementException */ protected boolean beforeGetCmpField() throws ManagementException { if (logger.isDebugEnabled()) { logger.debug("beforeGetCmpField() on profile with name "+profileName+" of table "+profileTable.getProfileTableName()); } return beforeNonSetCmpField(); } /** * Logic to execute after invoking a cmp getter method on the mbean * @param activatedTransaction if the method beforeGetCmpField() initiated or resumed a transaction * @throws ManagementException */ protected void afterGetCmpField(boolean activatedTransaction) throws ManagementException { if (logger.isDebugEnabled()) { logger.debug("afterGetCmpField( activatedTransaction = "+activatedTransaction+" ) on profile with name "+profileName+" of table "+profileTable.getProfileTableName()); } afterNonSetCmpField(activatedTransaction); } private boolean beforeNonSetCmpField() throws ManagementException { if (logger.isDebugEnabled()) { logger.debug("beforeManagementMethodInvocation() on profile with name "+profileName+" of table "+profileTable.getProfileTableName()); } if (isProfileWriteable()) { try { sleeContainer.getTransactionManager().resume(transaction); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } return true; } else { // not in write mode, create transaction just for this invocation try { sleeContainer.getTransactionManager().begin(); return true; } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } } private void afterNonSetCmpField(boolean activatedTransaction) throws ManagementException { if (logger.isDebugEnabled()) { logger.debug("afterManagementMethodInvocation( activatedTransaction = "+activatedTransaction+" ) on profile with name "+profileName+" of table "+profileTable.getProfileTableName()); } if (activatedTransaction) { if (isProfileWriteable()) { try { sleeContainer.getTransactionManager().suspend(); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } else { try { final SleeTransactionManager txManager = sleeContainer.getTransactionManager(); if (txManager.getRollbackOnly()) { txManager.rollback(); } else { txManager.commit(); } } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } } } } /** * Logic to execute before invoking a management method on the mbean * @return true if the method initiated or resumed a transaction * @throws ManagementException */ protected boolean beforeManagementMethodInvocation() throws ManagementException { if (logger.isDebugEnabled()) { logger.debug("beforeManagementMethodInvocation() on profile with name "+profileName+" of table "+profileTable.getProfileTableName()); } return beforeNonSetCmpField(); } /** * Logic to execute after invoking a management method on the mbean * @param activatedTransaction if the method beforeManagementMethodInvocation() initiated or resumed a transaction * @throws ManagementException */ protected void afterManagementMethodInvocation(boolean activatedTransaction) throws ManagementException { if (logger.isDebugEnabled()) { logger.debug("afterManagementMethodInvocation( activatedTransaction = "+activatedTransaction+" ) on profile with name "+profileName+" of table "+profileTable.getProfileTableName()); } afterNonSetCmpField(activatedTransaction); } /** * Handles a {@link Throwable}, which was the result of a management method invocation * @param t * @throws ProfileImplementationException * @throws InvalidStateException * @throws ManagementException */ protected void throwableOnManagementMethodInvocation(Throwable t) throws ProfileImplementationException, InvalidStateException, ManagementException { if (t instanceof ProfileImplementationException) { throw (ProfileImplementationException)t; } else if (t instanceof InvalidStateException) { throw (InvalidStateException)t; } else if (t instanceof ReadOnlyProfileException) { throw new InvalidStateException(t.getMessage()); } else if (t instanceof ManagementException) { throw (ManagementException)t; } else if (t instanceof RuntimeException) { try { getProfileObject().invalidateObject(); sleeContainer.getTransactionManager().setRollbackOnly(); } catch (Throwable e) { throw new ManagementException(e.getMessage(),e); } throw new ProfileImplementationException(t); } else { // checked exception throw new ProfileImplementationException(t); } } }