/*
* 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.transform.description;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ProcessType;
import org.jboss.as.controller.RunningMode;
import org.jboss.as.controller.logging.ControllerLogger;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.registry.Resource.ResourceEntry;
import org.jboss.as.controller.transform.OperationResultTransformer;
import org.jboss.as.controller.transform.OperationTransformer;
import org.jboss.as.controller.transform.OperationTransformer.TransformedOperation;
import org.jboss.as.controller.transform.ResourceTransformationContext;
import org.jboss.as.controller.transform.TransformationContext;
import org.jboss.as.controller.transform.TransformationTarget;
import org.jboss.as.controller.transform.TransformersLogger;
import org.jboss.dmr.ModelNode;
/**
*
* @author Emanuel Muckenhuber
*/
abstract class TransformationRule {
abstract void transformOperation(ModelNode operation, PathAddress address, ChainedOperationContext context) throws OperationFailedException;
abstract void transformResource(Resource resource, PathAddress address, ChainedResourceContext context) throws OperationFailedException;
static ModelNode cloneAndProtect(ModelNode modelNode) {
ModelNode clone = modelNode.clone();
clone.protect();
return clone;
}
abstract static class AbstractChainedContext {
private final TransformationContextWrapper context;
protected AbstractChainedContext(final TransformationContext context) {
this.context = new TransformationContextWrapper(context);
}
protected TransformationContext getContext() {
return context;
}
void setImmutableResource(boolean immutable) {
context.setImmutableResource(immutable);
}
}
abstract static class ChainedOperationContext extends AbstractChainedContext {
private final List<OperationTransformer.TransformedOperation> transformed = new ArrayList<OperationTransformer.TransformedOperation>();
private ModelNode lastOperation;
protected ChainedOperationContext(TransformationContext context) {
super(context);
}
protected void recordTransformedOperation(OperationTransformer.TransformedOperation operation) {
lastOperation = operation.getTransformedOperation();
transformed.add(operation);
}
void invokeNext(ModelNode operation) throws OperationFailedException {
invokeNext(new OperationTransformer.TransformedOperation(operation, OperationResultTransformer.ORIGINAL_RESULT));
}
abstract void invokeNext(OperationTransformer.TransformedOperation transformedOperation) throws OperationFailedException;
protected OperationTransformer.TransformedOperation createOp() {
if(transformed.size() == 1) {
return transformed.get(0);
}
return new ChainedTransformedOperation(lastOperation, transformed);
}
}
abstract static class ChainedResourceContext extends AbstractChainedContext {
protected ChainedResourceContext(ResourceTransformationContext context) {
super(context);
}
abstract void invokeNext(Resource resource) throws OperationFailedException;
}
private static class TransformationContextWrapper implements TransformationContext {
private final TransformationContext delegate;
private volatile boolean immutable;
private TransformationContextWrapper(TransformationContext delegate) {
this.delegate = delegate;
}
@Override
public TransformationTarget getTarget() {
return delegate.getTarget();
}
@Override
public ProcessType getProcessType() {
return delegate.getProcessType();
}
@Override
public RunningMode getRunningMode() {
return delegate.getRunningMode();
}
@Override
public ImmutableManagementResourceRegistration getResourceRegistration(PathAddress address) {
return delegate.getResourceRegistration(address);
}
@Override
public ImmutableManagementResourceRegistration getResourceRegistrationFromRoot(PathAddress address) {
return delegate.getResourceRegistrationFromRoot(address);
}
@Override
public Resource readResource(PathAddress address) {
Resource resource = delegate.readResource(address);
if (resource != null) {
return immutable ? new ProtectedModelResource<Resource>(resource) : resource;
}
return null;
}
@Override
public Resource readResourceFromRoot(PathAddress address) {
Resource resource = delegate.readResourceFromRoot(address);
if (resource != null) {
return immutable ? new ProtectedModelResource<Resource>(resource) : resource;
}
return null;
}
@Override
public TransformersLogger getLogger() {
return delegate.getLogger();
}
void setImmutableResource(boolean immutable) {
this.immutable = immutable;
}
@Override
public <T> T getAttachment(OperationContext.AttachmentKey<T> key) {
return delegate.getAttachment(key);
}
@Override
public <T> T attach(OperationContext.AttachmentKey<T> key, T value) {
return delegate.attach(key, value);
}
@Override
public <T> T attachIfAbsent(OperationContext.AttachmentKey<T> key, T value) {
return delegate.attachIfAbsent(key, value);
}
@Override
public <T> T detach(OperationContext.AttachmentKey<T> key) {
return delegate.detach(key);
}
}
private static class ChainedTransformedOperation extends OperationTransformer.TransformedOperation {
private final List<OperationTransformer.TransformedOperation> delegates;
private volatile String failure;
private volatile boolean initialized;
public ChainedTransformedOperation(final ModelNode transformedOperation, final List<OperationTransformer.TransformedOperation> delegates) {
super(transformedOperation, null);
this.delegates = delegates;
}
@Override
public ModelNode getTransformedOperation() {
return super.getTransformedOperation();
}
@Override
public OperationResultTransformer getResultTransformer() {
return this;
}
@Override
public boolean rejectOperation(ModelNode preparedResult) {
for (OperationTransformer.TransformedOperation delegate : delegates) {
if (delegate.rejectOperation(preparedResult)) {
failure = delegate.getFailureDescription();
initialized = true; //See comment in getFailureDescription()
return true;
}
}
return false;
}
@Override
public String getFailureDescription() {
//In real life this will always be initialized by the transforming proxy before anyone calls this method
//For testing we call it directly from ModelTestUtils
if (!initialized) {
for (TransformedOperation delegate : delegates) {
String failure = delegate.getFailureDescription();
if (failure != null) {
return failure;
}
}
}
return failure;
}
@Override
public ModelNode transformResult(ModelNode result) {
ModelNode currentResult = result;
final int size = delegates.size();
for (int i = size - 1 ; i >= 0 ; --i) {
currentResult = delegates.get(i).transformResult(currentResult);
}
return currentResult;
}
}
/**
* Implementation of resource that returns an unmodifiable model
*/
private static class ProtectedModelResource<T extends Resource> implements Resource {
protected T delegate;
ProtectedModelResource(T delegate){
this.delegate = delegate;
}
@Override
public ModelNode getModel() {
return TransformationRule.cloneAndProtect(delegate.getModel());
}
@Override
public void writeModel(ModelNode newModel) {
throw ControllerLogger.ROOT_LOGGER.immutableResource();
}
@Override
public boolean isModelDefined() {
return delegate.isModelDefined();
}
@Override
public boolean hasChild(PathElement element) {
return delegate.hasChild(element);
}
@Override
public Resource getChild(PathElement element) {
Resource resource = delegate.getChild(element);
if (resource != null) {
return new ProtectedModelResource<Resource>(resource);
}
return null;
}
@Override
public Resource requireChild(PathElement element) {
Resource resource = delegate.requireChild(element);
if (resource != null) {
return new ProtectedModelResource<Resource>(resource);
}
return null;
}
@Override
public boolean hasChildren(String childType) {
return delegate.hasChildren(childType);
}
@Override
public Resource navigate(PathAddress address) {
Resource resource = delegate.navigate(address);
if (resource != null) {
return new ProtectedModelResource<Resource>(resource);
}
return null;
}
@Override
public Set<String> getChildTypes() {
return delegate.getChildTypes();
}
@Override
public Set<String> getChildrenNames(String childType) {
return delegate.getChildrenNames(childType);
}
@Override
public Set<ResourceEntry> getChildren(String childType) {
Set<ResourceEntry> children = delegate.getChildren(childType);
if (children != null) {
Set<ResourceEntry> protectedChildren = new LinkedHashSet<Resource.ResourceEntry>();
for (ResourceEntry entry : children) {
protectedChildren.add(new ProtectedModelResourceEntry(entry));
}
return protectedChildren;
}
return null;
}
@Override
public void registerChild(PathElement address, Resource resource) {
throw ControllerLogger.ROOT_LOGGER.immutableResource();
}
@Override
public void registerChild(PathElement address, int index, Resource resource) {
throw ControllerLogger.ROOT_LOGGER.immutableResource();
}
@Override
public Resource removeChild(PathElement address) {
throw ControllerLogger.ROOT_LOGGER.immutableResource();
}
@Override
public boolean isRuntime() {
return delegate.isRuntime();
}
@Override
public boolean isProxy() {
return delegate.isProxy();
}
@Override
public Set<String> getOrderedChildTypes() {
return delegate.getOrderedChildTypes();
}
public Resource clone() {
return new ProtectedModelResource<Resource>(delegate.clone());
}
}
private static class ProtectedModelResourceEntry extends ProtectedModelResource<ResourceEntry> implements ResourceEntry {
ProtectedModelResourceEntry(ResourceEntry delegate){
super(delegate);
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public PathElement getPathElement() {
return delegate.getPathElement();
}
}
}