package org.mobicents.slee.container.management.jmx; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import javax.management.ListenerNotFoundException; import javax.management.MBeanNotificationInfo; import javax.management.MBeanServer; import javax.management.NotCompliantMBeanException; import javax.management.NotificationBroadcasterSupport; import javax.management.NotificationFilter; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.StandardMBean; import javax.slee.InvalidArgumentException; import javax.slee.InvalidStateException; import javax.slee.management.ManagementException; import javax.slee.management.SleeManagementMBean; import javax.slee.management.SleeState; import javax.slee.management.SleeStateChangeNotification; import javax.slee.management.UnrecognizedSubsystemException; import javax.slee.usage.UnrecognizedUsageParameterSetNameException; import org.apache.log4j.Logger; import org.mobicents.slee.container.SleeContainer; import org.mobicents.slee.container.Version; import org.mobicents.slee.container.management.ResourceManagement; import org.mobicents.slee.resource.ResourceAdaptorObjectState; import org.mobicents.slee.runtime.activity.ActivityContext; import org.mobicents.slee.runtime.activity.ActivityContextHandle; import org.mobicents.slee.runtime.activity.ActivityType; import org.mobicents.slee.runtime.transaction.SleeTransactionManager; /** * Implementation of the Slee Management MBean for SLEE 1.1 specs * * @author M. Ranganathan * @author Ivelin Ivanov * @author Eduardo Martins * */ public class SleeManagementMBeanImpl extends StandardMBean implements SleeManagementMBeanImplMBean { private MBeanServer mbeanServer; private ObjectName activityManagementMBean; private ObjectName sbbEntitiesMBean; private static Logger logger; private ObjectName deploymentMBean; private SleeContainer sleeContainer; private ObjectName profileProvisioningMBean; private ObjectName serviceManagementMBean; private ObjectName rmiServerInterfaceMBean; private ObjectName resourceManagementMBean; private NotificationBroadcasterSupport notificationBroadcaster = new NotificationBroadcasterSupport(); /** counter for the number of slee state change notifications sent out */ private long sleeStateChangeSequenceNumber = 1; /** * The array of MBean notifications that this MBean broadcasts * */ private static final MBeanNotificationInfo[] MBEAN_NOTIFICATIONS; /** * Holds the startup time of the Mobicents SAR. Assumes that this MBean is * instantiated first and started last within the scope of Mobicents.sar */ private long startupTime; private boolean isFullSleeStop = true; private ObjectName objectName; static { MBEAN_NOTIFICATIONS = new MBeanNotificationInfo[] { new MBeanNotificationInfo( new String[] { SleeStateChangeNotification.class.getName() }, SleeManagementMBean.SLEE_STATE_CHANGE_NOTIFICATION_TYPE, "SLEE 1.0 Spec, #14.6, Each time the operational state of the SLEE changes, " + "the SleeManagementMBean object generates a SLEE state change notification.") }; try { logger = Logger.getLogger(SleeManagementMBean.class); } catch (Exception ex) { logger.error("error initializing slee management mbean"); } } /** * Default constructor * * @throws Exception */ public SleeManagementMBeanImpl(SleeContainer sleeContainer) throws NotCompliantMBeanException { super(SleeManagementMBeanImplMBean.class); startupTime = System.currentTimeMillis(); logger.info(generateMessageWithLogo("starting")); this.sleeContainer = sleeContainer; } /** * * @return the ObjectName of the SleeManagementMBean */ public ObjectName getObjectName() { return this.objectName; } /** * @return the current state of the SLEE Container * * @see javax.slee.management.SleeManagementMBean#getState() */ public SleeState getState() throws ManagementException { return this.sleeContainer.getSleeState(); } /** * Start the SLEE container * * @see javax.slee.management.SleeManagementMBean#start() */ public void start() throws InvalidStateException, ManagementException { if (this.sleeContainer.getSleeState() == SleeState.STARTING || this.sleeContainer.getSleeState() == SleeState.RUNNING || this.sleeContainer.getSleeState() == SleeState.STOPPING) throw new InvalidStateException( "SLEE is already in an active state"); try { startSleeContainer(); } catch (Throwable ex) { throw new ManagementException(ex.getMessage(), ex); } } /** * Gracefully stop the SLEE. Should do it in a non-blocking manner. * * @see javax.slee.management.SleeManagementMBean#stop() */ public void stop() throws InvalidStateException, ManagementException { try { if (this.sleeContainer.getSleeState() == SleeState.STOPPING || this.sleeContainer.getSleeState() == SleeState.STOPPED) throw new InvalidStateException( "SLEE is already stopping or stopped."); stopSleeContainer(); } catch (Exception ex) { if (ex instanceof InvalidStateException) throw (InvalidStateException) ex; else throw new ManagementException("Failed stopping SLEE container", ex); } } /** * Shutdown the SLEE processes. The spec requires that System.exit() be * called before this methods returns. We are not convinced this is * necessary yet. A trivial implementation would be to make a call to the * JBoss server shutdown() * * @see javax.slee.management.SleeManagementMBean#shutdown() */ public void shutdown() throws InvalidStateException, ManagementException { if (this.sleeContainer.getSleeState() != SleeState.STOPPED) throw new InvalidStateException("SLEE is not in STOPPED state."); try { // NOP. Because I am not convinced we need to shut JBoss down. } catch (Exception ex) { throw new ManagementException(ex.getMessage()); } } /** * return the ObjectName of the DeploymentMBean * * @see javax.slee.management.SleeManagementMBean#getDeploymentMBean() */ public ObjectName getDeploymentMBean() { return this.deploymentMBean; } /** * set the ObjectName of the DeploymentMBean * * @see javax.slee.management.SleeManagementMBean#getDeploymentMBean() */ public void setDeploymentMBean(ObjectName newDM) { this.deploymentMBean = newDM; } /** * return the ObjectName of the ServiceManagementMBean * * @see javax.slee.management.SleeManagementMBean#getServiceManagementMBean() */ public ObjectName getServiceManagementMBean() { return this.serviceManagementMBean; } /** * set the ObjectName of the ServiceManagementMBean * * @see javax.slee.management.SleeManagementMBean#setServiceManagementMBean() */ public void setServiceManagementMBean(ObjectName newSMM) { this.serviceManagementMBean = newSMM; } /* * return the ObjectName of the ProfileProvisioningMBean * * @see * javax.slee.management.SleeManagementMBean#getProfileProvisioningMBean() */ public ObjectName getProfileProvisioningMBean() { return this.profileProvisioningMBean; } /** * set the ObjectName of the ProfileProvisioningMBean * * @see javax.slee.management.SleeManagementMBean#setProfileProvisioningMBean() */ public void setProfileProvisioningMBean(ObjectName newPPM) { this.profileProvisioningMBean = newPPM; } /* * return the ObjectName of the TraceMBean * * @see javax.slee.management.SleeManagementMBean#getTraceMBean() */ public ObjectName getTraceMBean() { return sleeContainer.getTraceMBean().getObjectName(); } // /** // * @param resourceAdaptorMBean The resourceAdaptorMBean to set. // * @deprecated Use setResourceManagementMBean // */ // public void setResourceAdaptorMBean(ObjectName resourceAdaptorMBean) { // this.resourceAdaptorMBean = resourceAdaptorMBean; // } public void setResourceManagementMBean(ObjectName resourceManagementMBean) { this.resourceManagementMBean = resourceManagementMBean; } // /** // * @return Returns the resourceAdaptorMBean. // * @deprecated Use getResourceManagementMBean // */ // public ObjectName getResourceAdaptorMBean() { // return resourceAdaptorMBean; // } public ObjectName getResourceManagementMBean() { return resourceManagementMBean; } /** * return the ObjectName of the AlarmMBean * * @see javax.slee.management.SleeManagementMBean#getAlarmMBean() */ public ObjectName getAlarmMBean() { return sleeContainer.getAlarmMBean().getObjectName(); } public ObjectName preRegister(MBeanServer mbs, ObjectName oname) throws Exception { this.mbeanServer = mbs; this.objectName = oname; return oname; } /* * (non-Javadoc) * * @see javax.management.MBeanRegistration#postRegister(java.lang.Boolean) */ public void postRegister(Boolean arg0) { } /* * (non-Javadoc) * * @see javax.management.MBeanRegistration#preDeregister() */ public void preDeregister() throws Exception { // TODO Auto-generated method stub } /* * (non-Javadoc) * * @see javax.management.MBeanRegistration#postDeregister() */ public void postDeregister() { // TODO Auto-generated method stub } /* * (non-Javadoc) * * @see * javax.management.NotificationBroadcaster#addNotificationListener(javax * .management.NotificationListener, javax.management.NotificationFilter, * java.lang.Object) */ public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException { notificationBroadcaster.addNotificationListener(listener, filter, handback); } /* * (non-Javadoc) * * @see * javax.management.NotificationBroadcaster#removeNotificationListener(javax * .management.NotificationListener) */ public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { notificationBroadcaster.removeNotificationListener(listener); } /* * (non-Javadoc) * * @see javax.management.NotificationBroadcaster#getNotificationInfo() */ public MBeanNotificationInfo[] getNotificationInfo() { return MBEAN_NOTIFICATIONS; } /* * (non-Javadoc) * * @see org.jboss.system.Service#create() */ public void create() throws Exception { // TODO Auto-generated method stub } /* * (non-Javadoc) * * @see org.jboss.system.Service#destroy() */ public void destroy() { // TODO Auto-generated method stub } /** * Start the SleeContainer and initialize the necessary resources * */ protected void startSleeContainer() throws Throwable { changeSleeState(SleeState.STARTING); boolean created = sleeContainer.getTransactionManager() .requireTransaction(); boolean rollback = true; try { // (Ivelin) the following check is symmetric to the one is stop(). // see the comments in stop() for more detail. if (isFullSleeStop) { sleeContainer.init(this, rmiServerInterfaceMBean); isFullSleeStop = false; } else { // re config event router sleeContainer.getEventRouter().config( MobicentsManagement.eventRouterExecutors, MobicentsManagement.monitoringUncommittedAcAttachs); } changeSleeState(SleeState.RUNNING); startResourceAdaptors(); if (sleeContainer.getCluster().isHeadMember()) { // only one node can do it sleeContainer.getServiceManagement().startActiveServicesActivities(); if (logger.isDebugEnabled()) { logger.debug("starting all profile table activities"); } sleeContainer.getSleeProfileTableManager() .startAllProfileTableActivities(); } rollback = false; } catch (Throwable ex) { logger.error("Error starting SLEE container", ex); throw ex; } finally { boolean started = false; try { if (created) { if (rollback) { sleeContainer.getTransactionManager().rollback(); } else { sleeContainer.getTransactionManager().commit(); started = true; } } else { started = true; } } catch (Exception e) { logger .error( "Error finishing transaction on SLEE container startup", e); } // if startup did not succeed, try to clean up resources if (sleeContainer.getSleeState() != SleeState.RUNNING || !started) stopSleeContainer(); } } private void startResourceAdaptors() { // inform all ra entities that we are starting the container final ResourceManagement resourceManagement = sleeContainer .getResourceManagement(); for (String entityName : resourceManagement .getResourceAdaptorEntities()) { try { resourceManagement.getResourceAdaptorEntity(entityName) .sleeRunning(); } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); } } } } /** * Stop the service container and clean up the resources it used. * */ protected void stopSleeContainer() throws Exception { changeSleeState(SleeState.STOPPING); logger.info(generateMessageWithLogo("stopping")); // (Ivelin) // If the stop() is invoked externally, skip further effort to stop all // services // only if the stop() is invoked as a result of undeployment of // mobicents.sar, then stop everything // the reason we need to do this is because MBean service dependecy is // not taken into account // with external stop(),start() invocation and as a result the start() // after stop() is not clean. // // The value of isFullSleeStop is controlled by SleeLifecycleMonitor if (isFullSleeStop) { sleeContainer.close(); // we do not want STOPPED state, because that is not desired // in a cluster situation and redeployment. // Only when the server is really crashing on all nodes, STOPPED // state // makes sense, but at that point it is not as relevant. // changeSleeState(SleeState.STOPPED); // Still, we want an indication that the container service concluded // stopping cycle logger.info(generateMessageWithLogo("stopped (HA)")); } else { scheduleStopped(); } } private void endAllServiceAndProfileTableActivities() { logger.info("Ending all service and profile table activities..."); final SleeTransactionManager sleeTransactionManager = sleeContainer.getTransactionManager(); boolean rb = true; try { sleeTransactionManager.begin(); // end all service and profile table activities for (ActivityContextHandle handle : sleeContainer .getActivityContextFactory() .getAllActivityContextsHandles()) { if (handle.getActivityType() == ActivityType.SERVICE || handle.getActivityType() == ActivityType.PTABLE) { try { if (logger.isDebugEnabled()) { logger.debug("Ending activity " + handle); } ActivityContext ac = sleeContainer .getActivityContextFactory() .getActivityContext(handle); if (ac != null) { ac.endActivity(); } } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug("Failed to end activity " + handle, e); } } } } rb = false; } catch (Exception e) { logger .error( "Exception while ending all service and profile table activities", e); } finally { try { if (rb) { sleeTransactionManager.rollback(); } else { sleeTransactionManager.commit(); } } catch (Exception e) { logger .error( "Error in tx management while ending all service and profile table activities", e); } } // wait all activites end boolean loop; do { loop = false; try { sleeTransactionManager.begin(); for (ActivityContextHandle handle : sleeContainer .getActivityContextFactory() .getAllActivityContextsHandles()) { if (handle.getActivityType() == ActivityType.SERVICE || handle.getActivityType() == ActivityType.PTABLE) { logger.info("Waiting for activity "+handle+" to end..."); loop = true; break; } } } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); } } finally { try { sleeTransactionManager.rollback(); } catch (Exception e) { logger.error("Error in tx management while stopping SLEE",e); } } if (loop) { try { // wait a sec Thread.sleep(1000); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } } while (loop); } private void stopResourceAdaptors() { logger.info("Stopping all active resource adaptors ..."); final SleeTransactionManager sleeTransactionManager = sleeContainer.getTransactionManager(); final ResourceManagement resourceManagement = sleeContainer .getResourceManagement(); boolean rb = true; try { sleeTransactionManager.begin(); // inform all ra entities that we are stopping the container for (String entityName : resourceManagement .getResourceAdaptorEntities()) { try { resourceManagement.getResourceAdaptorEntity(entityName) .sleeStopping(); } catch (Exception e) { logger.error(e.getMessage(),e); } } rb = false; } catch (Exception e) { logger .error( "Exception while stopping resource adaptors", e); } finally { try { if (rb) { sleeTransactionManager.rollback(); } else { sleeTransactionManager.commit(); } } catch (Exception e) { logger .error( "Error in tx management while stopping resource adaptors", e); } } // wait till all ra entity objects are stopped boolean loop; do { loop = false; for (String entityName : resourceManagement .getResourceAdaptorEntities()) { try { if (resourceManagement.getResourceAdaptorEntity( entityName).getResourceAdaptorObject() .getState() == ResourceAdaptorObjectState.STOPPING) { logger.info("Waiting for ra entity "+entityName+" to stop..."); loop = true; } } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); } } } if (loop) { try { // wait a sec Thread.sleep(1000); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } } while (loop); } /** * Setup a polling process to wait until all ActivityContexts ended. Then * set the SLEE container in STOPPED state. * */ protected void scheduleStopped() { if (logger.isDebugEnabled()) { logger.debug("schedule stopped"); } ExecutorService exec = Executors.newSingleThreadExecutor(); Runnable acStateChecker = new Runnable() { public void run() { if (sleeContainer.getCluster().isHeadMember()) { // only one node can do it endAllServiceAndProfileTableActivities(); } stopResourceAdaptors(); changeSleeState(SleeState.STOPPED); } }; try { // if jboss as is shutting down then we wait till servers stops Boolean inShutdown = (Boolean) mbeanServer.getAttribute( new ObjectName("jboss.system:type=Server"), "InShutdown"); Future<?> future = exec.submit(acStateChecker); if (inShutdown) { future.get(); } } catch (Exception e) { logger .error( "Failed scheduling polling task for STOPPED state. The SLEE Container may remain in STOPPING state.", e); } } /** * Changes the SLEE container state and emits JMX notifications * * @param newState */ protected void changeSleeState(SleeState newState) { SleeState oldState = sleeContainer.getSleeState(); sleeContainer.setSleeState(newState); notificationBroadcaster .sendNotification(new SleeStateChangeNotification(this, newState, oldState, sleeStateChangeSequenceNumber++)); if (newState == SleeState.RUNNING) { String timerSt = ""; if (isFullSleeStop) { startupTime = System.currentTimeMillis() - startupTime; long startupSec = startupTime / 1000; long startupMillis = startupTime % 1000; timerSt = " in " + startupSec + "s:" + startupMillis + "ms"; } logger.info(generateMessageWithLogo("started" + timerSt)); } else { if (newState == SleeState.STOPPED) { logger.info(generateMessageWithLogo("stopped")); } } } public void setFullSleeStop(boolean b) { isFullSleeStop = b; } /** * Indicates whether Mobicents SLEE is simply marked as STOPPED or all its * services actually stopped. The distinction is important due to the way * MBean service dependencies are handled on mobicents.sar undeploy vs. * externally calling SleeManagementMBean.stop() */ public boolean isFullSleeStop() { return isFullSleeStop; } public ObjectName getActivityManagementMBean() { return activityManagementMBean; } public void setActivityManagementMBean(ObjectName activityManagementMBean) { this.activityManagementMBean = activityManagementMBean; } public ObjectName getSbbEntitiesMBean() { return sbbEntitiesMBean; } public void setSbbEntitiesMBean(ObjectName sbbEntitiesMBean) { this.sbbEntitiesMBean = sbbEntitiesMBean; } public ObjectName getRmiServerInterfaceMBean() { return rmiServerInterfaceMBean; } public void setRmiServerInterfaceMBean(ObjectName rmiServerInterfaceMBean) { this.rmiServerInterfaceMBean = rmiServerInterfaceMBean; } // ah ah private static final String rLogo = " ><><><><><><><><>< "; private static final String lLogo = rLogo; private String generateMessageWithLogo(String message) { return lLogo + getSleeName() + ' ' + getSleeVersion() + ' ' + message + rLogo; } public String getSleeName() { String name = Version.instance.getProperty("name"); if (name != null) { return name; } else { return "Mobicents JAIN SLEE Server"; } } public String getSleeVendor() { String vendor = Version.instance.getProperty("vendor"); if (vendor != null) { return vendor; } else { return "JBoss, a division of Red Hat"; } } public String getSleeVersion() { String version = Version.instance.getProperty("version"); if (version != null) { return version; } else { return "2.0"; } } // no subsystems defined private static final String DUMMY_SUBSYSTEM = "FooSubsystem"; public String[] getSubsystems() throws ManagementException { return new String[] { DUMMY_SUBSYSTEM }; } public ObjectName getUsageMBean(String arg0) throws NullPointerException, UnrecognizedSubsystemException, InvalidArgumentException, ManagementException { if (arg0 == null) { throw new NullPointerException(); } if (arg0.equals(DUMMY_SUBSYSTEM)) { throw new InvalidArgumentException(); } else { throw new UnrecognizedSubsystemException(); } } public ObjectName getUsageMBean(String arg0, String arg1) throws NullPointerException, UnrecognizedSubsystemException, InvalidArgumentException, UnrecognizedUsageParameterSetNameException, ManagementException { if (arg0 == null) { throw new NullPointerException(); } if (arg0.equals(DUMMY_SUBSYSTEM)) { throw new InvalidArgumentException(); } else { throw new UnrecognizedSubsystemException(); } } public ObjectName getUsageNotificationManagerMBean(String arg0) throws NullPointerException, UnrecognizedSubsystemException, InvalidArgumentException, ManagementException { if (arg0 == null) { throw new NullPointerException(); } if (arg0.equals(DUMMY_SUBSYSTEM)) { throw new InvalidArgumentException(); } else { throw new UnrecognizedSubsystemException(); } } public String[] getUsageParameterSets(String arg0) throws NullPointerException, UnrecognizedSubsystemException, InvalidArgumentException, ManagementException { if (arg0 == null) { throw new NullPointerException(); } if (arg0.equals(DUMMY_SUBSYSTEM)) { throw new InvalidArgumentException(); } else { throw new UnrecognizedSubsystemException(); } } public boolean hasUsage(String arg0) throws NullPointerException, UnrecognizedSubsystemException, ManagementException { if (arg0 == null) { throw new NullPointerException(); } if (arg0.equals(DUMMY_SUBSYSTEM)) { return false; } else { throw new UnrecognizedSubsystemException(); } } }