/* * #%L * Nazgul Project: nazgul-core-jmx-api * %% * Copyright (C) 2010 - 2017 jGuru Europe AB * %% * Licensed under the jGuru Europe AB license (the "License"), based * on Apache License, Version 2.0; you may not use this file except * in compliance with the License. * * You may obtain a copy of the License at * * http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% * */ package se.jguru.nazgul.core.jmx.api; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.management.AttributeChangeNotificationFilter; import javax.management.Notification; import javax.management.NotificationBroadcaster; import javax.management.NotificationEmitter; /** * Abstract implementation of a Suspendable AbstractMBean, sporting LifecycleState * validation for the suspend and resume methods. * * @author <a href="mailto:lj@jguru.se">Lennart Jörelid, jGuru Europe AB</a> */ public abstract class AbstractSuspendableMBean extends AbstractMBean implements Suspendable { // Our log private static final Logger log = LoggerFactory.getLogger(AbstractSuspendableMBean.class); /** * The attribute name in the Notification emitted when this AbstractSuspendableMBean is successfully suspended. */ public static final String SUSPENDED_ATTRIBUTENAME = "suspended"; /** * The attribute name in the Notification emitted when this AbstractSuspendableMBean is successfully resumed. */ public static final String RESUMED_ATTRIBUTENAME = "resumed"; /** * <p>Convenience constructor, creating a new AbstractMBean wrapping the supplied data. * All NotificationEmitter operations executed in this AbstractMBean are delegated to * the provided NotificationEmitter object. Thus, all JMX notifications fired by this * AbstractMBean are simply delegated to the supplied NotificationEmitter.</p> * <p>This constructor must be called from a subclass that implements the provided {@code mbeanInterface}.</p> * * @param mbeanInterface a StandardMBean interface. * @param delegate A non-null NotificationEmitter to which this AbstractMBean will delegate all * NotificationEmitter operations. * @throws IllegalArgumentException if the {@code mbeanInterface} does not follow JMX design patterns for * Management Interfaces, or if {@code this} does not implement the * specified * interface, or if {@code delegate} is null. * @see javax.management.StandardEmitterMBean#StandardEmitterMBean(Class, boolean, * javax.management.NotificationEmitter) */ protected AbstractSuspendableMBean(final Class<?> mbeanInterface, final NotificationEmitter delegate) { super(mbeanInterface, delegate); } /** * <p>Creates an AbstractMBean with the management interface {@code mbeanInterface}, and where notifications are * handled by the given {@code NotificationEmitter}. This constructor can be used to make either Standard MBeans * or MXBeans. The resultant MBean implements the {@code NotificationEmitter} interface by forwarding its methods * to {@code delegate}.</p> * <p>If {@code delegate} is an instance of {@code NotificationBroadcasterSupport} then the MBean's * {@link javax.management.NotificationBroadcasterSupport#sendNotification(Notification)} method will delegate * its invocation to {@code delegate.}{@link javax.management.NotificationBroadcasterSupport#sendNotification(Notification)}.</p> * <p>The array returned by {@link #getNotificationInfo()} on the new MBean is a copy of the array returned by * {@code emitter.}{@link NotificationBroadcaster#getNotificationInfo()} at the time of * construction. If the array returned by {@code emitter.getNotificationInfo()} later changes, that will have no * effect on this object's {@code getNotificationInfo()}.</p> * <p>This constructor must be called from a subclass that implements the given {@code mbeanInterface}.</p> * * @param mbeanInterface a StandardMBean interface. * @param isMXBean If true, the {@code mbeanInterface} parameter * names an MXBean interface and the resultant MBean is an MXBean. * @param delegate A non-null NotificationEmitter to which this AbstractMBean will delegate all * NotificationEmitter operations. * @throws IllegalArgumentException if the {@code mbeanInterface} * does not follow JMX design patterns for Management Interfaces, or * if {@code this} does not implement the specified interface, or * if {@code delegate} is null. */ protected AbstractSuspendableMBean(final Class<?> mbeanInterface, final boolean isMXBean, final NotificationEmitter delegate) { super(mbeanInterface, isMXBean, delegate); } /** * {@inheritDoc} */ @Override public final boolean suspend() throws IllegalStateException { // Check sanity if (getState() == LifecycleState.STOPPED) { if (log.isInfoEnabled()) { log.info("AbstractSuspendableMBean [" + getClass().getName() + "] was already in [" + LifecycleState.STOPPED + "]. Aborting suspend."); } // All done. return true; } if (getState() != LifecycleState.STARTED) { throw new IllegalStateException("Cannot suspend from state [" + getState() + "]. (Required: " + LifecycleState.STARTED + ")."); } // Delegate & make the state transition if (suspendMBeanOperations() && performStateTransition(false, LifecycleState.STARTED, LifecycleState.STOPPED)) { // Send an explicit "suspend successful" message sendAttributeChangeEvent(SUSPENDED_ATTRIBUTENAME, "MBean Suspended. Type: [" + getClass().getName() + "]", LifecycleState.class, LifecycleState.STARTED, LifecycleState.STOPPED); // All done. return true; } // Don't make a state transition. return false; } /** * {@code} */ @Override public final boolean resume() throws IllegalStateException { // Check sanity if (getState() == LifecycleState.STARTED) { if (log.isInfoEnabled()) { log.info("AbstractSuspendableMBean [" + getClass().getName() + "] was already in [" + LifecycleState.STARTED + "]. Aborting resume."); } // All done. return true; } if (getState() != LifecycleState.STOPPED) { throw new IllegalStateException("Cannot resume from state [" + getState() + "]. (Required: " + LifecycleState.STOPPED + ")."); } // Delegate if (resumeMBeanOperations() && performStateTransition(false, LifecycleState.STOPPED, LifecycleState.STARTED)) { // Send an explicit "resume successful" message sendAttributeChangeEvent(RESUMED_ATTRIBUTENAME, "MBean Resumed. Type: [" + getClass().getName() + "]", LifecycleState.class, LifecycleState.STOPPED, LifecycleState.STARTED); // All done. return true; } // Don't make a state transition. return false; } /** * Implement this method to define what should be done to suspend (operations within) this MBean. * Invoked from the {@code suspend()} method; this method should not throw Exceptions. * * @return {@code true} if this AbstractSuspendableMBean was successfully * suspended, and {@code false} otherwise. */ protected abstract boolean suspendMBeanOperations(); /** * Implement this method to define what should be done to resume (operations within) this MBean. * Invoked from the {@code resume()} method; this method should not throw Exceptions. * * @return {@code true} if this AbstractSuspendableMBean was successfully * resumed, and {@code false} otherwise. */ protected abstract boolean resumeMBeanOperations(); /** * @return An AttributeChangeNotificationFilter which will listen only to state change events emitted by * an AbstractSuspendableMBean subclass (e.g. AttributeChangeEvent with attributeName set to the value of * constants {@code SUSPENDED_ATTRIBUTENAME} or {@code RESUMED_ATTRIBUTENAME}). */ public static AttributeChangeNotificationFilter getSuspendResumeFilter() { final AttributeChangeNotificationFilter toReturn = new AttributeChangeNotificationFilter(); toReturn.enableAttribute(AbstractSuspendableMBean.SUSPENDED_ATTRIBUTENAME); toReturn.enableAttribute(AbstractSuspendableMBean.RESUMED_ATTRIBUTENAME); // All done. return toReturn; } }