/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, 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.jboss.as.controller;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import org.jboss.as.controller.capability.RuntimeCapability;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceName;
/**
* Abstract remove step handler that simply removes a service. If the operation is rolled
* back it delegates the rollback to the corresponding add operations
* {@link AbstractAddStepHandler#performRuntime(OperationContext, org.jboss.dmr.ModelNode, org.jboss.dmr.ModelNode)}
* method.
*
* @author Stuart Douglas
*/
public class ServiceRemoveStepHandler extends AbstractRemoveStepHandler {
private static final RuntimeCapability[] NO_CAPABILITIES = new RuntimeCapability[0];
private final ServiceName baseServiceName;
private final AbstractAddStepHandler addOperation;
private final Set<RuntimeCapability> unavailableCapabilities;
/**
* Creates a {@code ServiceRemoveStepHandler}.
* @param baseServiceName base name to remove. Cannot be {@code null} unless {@code unavailableCapabilities} are provided
* @param addOperation the add operation to use to rollback service removal. Cannot be {@code null}
* @param unavailableCapabilities capabilities that will no longer be available once the remove occurs. Any services
* {@link RuntimeCapability#getCapabilityServiceValueType() exposed by the capabilities} will also be removed
*/
public ServiceRemoveStepHandler(final ServiceName baseServiceName, final AbstractAddStepHandler addOperation, final RuntimeCapability ... unavailableCapabilities) {
super(unavailableCapabilities);
this.baseServiceName = baseServiceName;
this.addOperation = addOperation;
this.unavailableCapabilities = new LinkedHashSet<>(Arrays.asList(unavailableCapabilities));
}
/**
* Creates a {@code ServiceRemoveStepHandler}.
* @param baseServiceName base name to remove. Cannot be {@code null}
* @param addOperation the add operation to use to rollback service removal. Cannot be {@code null}
*/
public ServiceRemoveStepHandler(final ServiceName baseServiceName, final AbstractAddStepHandler addOperation) {
this(baseServiceName, addOperation, NO_CAPABILITIES);
}
/**
* Creates a {@code ServiceRemoveStepHandler}.
* @param addOperation the add operation to use to rollback service removal. Cannot be {@code null}
* @param unavailableCapabilities capabilities that will no longer be available once the remove occurs. Any services
* {@link RuntimeCapability#getCapabilityServiceValueType() exposed by the capabilities} will also be removed.
* Cannot be {@code null} or empty.
*/
public ServiceRemoveStepHandler(final AbstractAddStepHandler addOperation, final RuntimeCapability ... unavailableCapabilities) {
this(null, addOperation, unavailableCapabilities);
}
protected ServiceRemoveStepHandler(final AbstractAddStepHandler addOperation) {
this(null, addOperation);
}
/**
* If the {@link OperationContext#isResourceServiceRestartAllowed() context allows resource removal},
* removes services; otherwise puts the process in reload-required state. The following services are
* removed:
* <ul>
* <li>The service named by the value returned from {@link #serviceName(String, PathAddress)}, if there is one</li>
* <li>The service names associated with any {@code unavailableCapabilities}
* passed to the constructor.</li>
* </ul>
*
* {@inheritDoc}
*/
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) {
if (context.isResourceServiceRestartAllowed()) {
final PathAddress address = context.getCurrentAddress();
final String name = address.getLastElement().getValue();
ServiceName nonCapabilityServiceName = serviceName(name, address);
if (nonCapabilityServiceName != null) {
context.removeService(serviceName(name, address));
}
Set<RuntimeCapability> capabilitySet = unavailableCapabilities.isEmpty() ? context.getResourceRegistration().getCapabilities() : unavailableCapabilities;
for (RuntimeCapability<?> capability : capabilitySet) {
if (capability.getCapabilityServiceValueType() != null) {
ServiceName sname;
if (capability.isDynamicallyNamed()) {
sname = capability.getCapabilityServiceName(name);
} else {
sname = capability.getCapabilityServiceName();
}
context.removeService(sname);
}
}
} else {
context.reloadRequired();
}
}
/**
* Is a runtime step required? By default his method delegates to the supplied {@link AbstractAddStepHandler}, if
* the add handler required a runtime step then most likely the remove step handler will also require one.
*
* @see org.jboss.as.controller.AbstractRemoveStepHandler#requiresRuntime(org.jboss.as.controller.OperationContext)
*/
@Override
protected boolean requiresRuntime(OperationContext context) {
return addOperation.requiresRuntime(context);
}
/**
* The service name to be removed. Can be overridden for unusual service naming patterns
* @param name The name of the resource being removed
* @param address The address of the resource being removed
* @return The service name to remove. May return {@code null} if only removal based on {@code unavailableCapabilities}
* passed to the constructor are to be performed
*/
protected ServiceName serviceName(String name, PathAddress address) {
return serviceName(name);
}
/**
* The service name to be removed. Can be overridden for unusual service naming patterns
* @param name The name of the resource being removed
* @return The service name to remove. May return {@code null} if only removal based on {@code unavailableCapabilities}
* passed to the constructor are to be performed
*/
protected ServiceName serviceName(final String name) {
return baseServiceName != null ? baseServiceName.append(name) : null;
}
/**
* If the {@link OperationContext#isResourceServiceRestartAllowed() context allows resource removal},
* attempts to restore services by invoking the {@code performRuntime} method on the @{code addOperation}
* handler passed to the constructor; otherwise puts the process in reload-required state.
*
* {@inheritDoc}
*/
@Override
protected void recoverServices(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
if (context.isResourceServiceRestartAllowed()) {
addOperation.performRuntime(context, operation, model);
} else {
context.revertReloadRequired();
}
}
}