/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file 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.wildfly.extension.messaging.activemq; import static org.jboss.as.controller.SimpleAttributeDefinitionBuilder.create; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.START; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STOP; import static org.wildfly.extension.messaging.activemq.CommonAttributes.NAME; import static org.wildfly.extension.messaging.activemq.logging.MessagingLogger.ROOT_LOGGER; import static org.jboss.dmr.ModelType.BOOLEAN; import org.apache.activemq.artemis.api.core.management.ActiveMQComponentControl; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.jboss.as.controller.AbstractRuntimeOnlyHandler; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationDefinition; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.SimpleAttributeDefinition; import org.jboss.as.controller.SimpleOperationDefinitionBuilder; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.descriptions.ResourceDescriptionResolver; import org.jboss.as.controller.logging.ControllerLogger; import org.jboss.as.controller.operations.validation.ParametersValidator; import org.jboss.as.controller.operations.validation.StringLengthValidator; import org.jboss.as.controller.registry.AttributeAccess; import org.jboss.as.controller.registry.ManagementResourceRegistration; import org.jboss.dmr.ModelNode; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; import org.wildfly.extension.messaging.activemq.logging.MessagingLogger; /** * Base class for {@link org.jboss.as.controller.OperationStepHandler} implementations for handlers that interact * with an implementation of a {@link ActiveMQComponentControl} subinterface to perform their function. This base class * handles a "start" and "stop" operation as well as a "read-attribute" call reading runtime attribute "started". * * @author Brian Stansberry (c) 2011 Red Hat Inc. */ public abstract class AbstractActiveMQComponentControlHandler<T extends ActiveMQComponentControl> extends AbstractRuntimeOnlyHandler { private static final SimpleAttributeDefinition STARTED = create(CommonAttributes.STARTED, BOOLEAN) .setFlags(AttributeAccess.Flag.STORAGE_RUNTIME) .build(); private ParametersValidator readAttributeValidator = new ParametersValidator(); protected AbstractActiveMQComponentControlHandler() { readAttributeValidator.registerValidator(NAME, new StringLengthValidator(1)); } @Override protected void executeRuntimeStep(OperationContext context, ModelNode operation) throws OperationFailedException { final String operationName = operation.require(OP).asString(); if (READ_ATTRIBUTE_OPERATION.equals(operationName)) { if (ActiveMQActivationService.ignoreOperationIfServerNotActive(context, operation)) { return; } readAttributeValidator.validate(operation); final String name = operation.require(NAME).asString(); if (STARTED.getName().equals(name)) { ActiveMQComponentControl control = getActiveMQComponentControl(context, operation, false); context.getResult().set(control.isStarted()); } else { handleReadAttribute(name, context, operation); } return; } if (ActiveMQActivationService.rollbackOperationIfServerNotActive(context, operation)) { return; } ActiveMQComponentControl control = null; boolean appliedToRuntime = false; Object handback = null; if (START.equals(operationName)) { control = getActiveMQComponentControl(context, operation, true); try { control.start(); appliedToRuntime = true; context.getResult(); } catch (Exception e) { context.getFailureDescription().set(e.getLocalizedMessage()); } } else if (STOP.equals(operationName)) { control = getActiveMQComponentControl(context, operation, true); try { control.stop(); appliedToRuntime = true; context.getResult(); } catch (Exception e) { context.getFailureDescription().set(e.getLocalizedMessage()); } } else { handback = handleOperation(operationName, context, operation); appliedToRuntime = handback != null; } OperationContext.RollbackHandler rh; if (appliedToRuntime) { final ActiveMQComponentControl rhControl = control; final Object rhHandback = handback; rh = new OperationContext.RollbackHandler() { @Override public void handleRollback(OperationContext context, ModelNode operation) { try { if (START.equals(operationName)) { rhControl.stop(); } else if (STOP.equals(operationName)) { rhControl.start(); } else { handleRevertOperation(operationName, context, operation, rhHandback); } } catch (Exception e) { ROOT_LOGGER.revertOperationFailed(e, getClass().getSimpleName(), operation.require(ModelDescriptionConstants.OP).asString(), PathAddress.pathAddress(operation.require(ModelDescriptionConstants.OP_ADDR))); } } }; } else { rh = OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER; } context.completeStep(rh); } public void registerAttributes(final ManagementResourceRegistration registry) { registry.registerReadOnlyAttribute(STARTED, this); } public void registerOperations(final ManagementResourceRegistration registry, final ResourceDescriptionResolver resolver) { final OperationDefinition startOp = new SimpleOperationDefinitionBuilder(START, resolver) .build(); registry.registerOperationHandler(startOp, this); final OperationDefinition stopOp = new SimpleOperationDefinitionBuilder(STOP, resolver) .build(); registry.registerOperationHandler(stopOp, this); } /** * Gets the {@link ActiveMQComponentControl} implementation used by this handler. * * @param activeMQServer the ActiveMQ server installed in the runtime * @param address the address being invoked * @return the runtime ActiveMQ control object associated with the given address */ protected abstract T getActiveMQComponentControl(ActiveMQServer activeMQServer, PathAddress address); protected abstract String getDescriptionPrefix(); /** * Hook to allow subclasses to handle read-attribute requests for attributes other than {@link CommonAttributes#STARTED}. * Implementations must not call any of the * {@link org.jboss.as.controller.OperationContext#completeStep(OperationContext.ResultHandler) context.completeStep variants}. * <p> * This default implementation just throws the exception returned by {@link #unsupportedAttribute(String)}. * </p> * * * @param attributeName the name of the attribute * @param context the operation context * @param operation * @throws OperationFailedException */ protected void handleReadAttribute(String attributeName, OperationContext context, ModelNode operation) throws OperationFailedException { unsupportedAttribute(attributeName); } /** * Hook to allow subclasses to handle operations other than {@code read-attribute}, {@code start} and * {@code stop}. Implementations must not call any of the * {@link org.jboss.as.controller.OperationContext#completeStep(OperationContext.ResultHandler) context.completeStep variants}. * <p> * This default implementation just throws the exception returned by {@link #unsupportedOperation(String)}. * </p> * * * @param operationName the name of the operation * @param context the operation context * @param operation the operation * * @return an object that can be passed back in {@link #handleRevertOperation(String, org.jboss.as.controller.OperationContext, org.jboss.dmr.ModelNode, Object)} * if the operation should be reverted. A value of {@code null} is an indication that no reversible * modification was made * @throws OperationFailedException */ protected Object handleOperation(String operationName, OperationContext context, ModelNode operation) throws OperationFailedException { unsupportedOperation(operationName); throw MessagingLogger.ROOT_LOGGER.unsupportedOperation(operationName); } /** * Hook to allow subclasses to handle revert changes made in * {@link #handleOperation(String, org.jboss.as.controller.OperationContext, org.jboss.dmr.ModelNode)}. * <p> * This default implementation does nothing. * </p> * * * @param operationName the name of the operation * @param context the operation context * @param operation the operation */ protected void handleRevertOperation(String operationName, OperationContext context, ModelNode operation, Object handback) { } /** * Return an ISE with a message saying support for the attribute was not properly implemented. This handler should * only be called if for a "read-attribute" operation if {@link #registerOperations(ManagementResourceRegistration, ResourceDescriptionResolver)} * registers the attribute, so a handler then not recognizing the attribute name would be a bug and this method * returns an exception highlighting that bug. * * @param attributeName the name of the attribute * @throws IllegalStateException an exception with a message indicating a bug in this handler */ protected final void unsupportedAttribute(final String attributeName) { // Bug throw MessagingLogger.ROOT_LOGGER.unsupportedAttribute(attributeName); } /** * Return an ISE with a message saying support for the operation was not properly implemented. This handler should * only be called if for a n operation if {@link #registerOperations(ManagementResourceRegistration, ResourceDescriptionResolver)} * registers it as a handler, so a handler then not recognizing the operation name would be a bug and this method * returns an exception highlighting that bug. * * @param operationName the name of the attribute * @throws IllegalStateException an exception with a message indicating a bug in this handler */ protected final void unsupportedOperation(final String operationName) { // Bug throw MessagingLogger.ROOT_LOGGER.unsupportedOperation(operationName); } /** * Gets the runtime ActiveMQ control object that can help service this request. * * @param context the operation context * @param operation the operation * @param forWrite {@code true} if this operation will modify the runtime; {@code false} if not. * @return the control object * @throws OperationFailedException */ protected final T getActiveMQComponentControl(final OperationContext context, final ModelNode operation, final boolean forWrite) throws OperationFailedException { final ServiceName artemisServiceName = MessagingServices.getActiveMQServiceName(PathAddress.pathAddress(operation.get(ModelDescriptionConstants.OP_ADDR))); ServiceController<?> artemisService = context.getServiceRegistry(forWrite).getService(artemisServiceName); ActiveMQServer server = ActiveMQServer.class.cast(artemisService.getValue()); PathAddress address = PathAddress.pathAddress(operation.require(OP_ADDR)); T control = getActiveMQComponentControl(server, address); if (control == null) { throw ControllerLogger.ROOT_LOGGER.managementResourceNotFound(address); } return control; } }