/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, 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.clustering.infinispan.subsystem;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.infinispan.transaction.LockingMode;
import org.jboss.as.clustering.controller.ManagementResourceRegistration;
import org.jboss.as.clustering.controller.MetricHandler;
import org.jboss.as.clustering.controller.Operations;
import org.jboss.as.clustering.controller.ResourceDescriptor;
import org.jboss.as.clustering.controller.SimpleResourceRegistration;
import org.jboss.as.clustering.controller.ResourceServiceHandler;
import org.jboss.as.clustering.controller.SimpleAliasEntry;
import org.jboss.as.clustering.controller.SimpleResourceServiceHandler;
import org.jboss.as.clustering.controller.transform.AttributeOperationTransformer;
import org.jboss.as.clustering.controller.transform.ChainedOperationTransformer;
import org.jboss.as.clustering.controller.transform.OperationTransformer;
import org.jboss.as.clustering.controller.transform.RequiredChildResourceDiscardPolicy;
import org.jboss.as.clustering.controller.transform.SimpleOperationTransformer;
import org.jboss.as.clustering.controller.validation.EnumValidatorBuilder;
import org.jboss.as.clustering.controller.validation.ParameterValidatorBuilder;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ModelVersion;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.client.helpers.MeasurementUnit;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.registry.AttributeAccess;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.transform.OperationResultTransformer;
import org.jboss.as.controller.transform.ResourceTransformationContext;
import org.jboss.as.controller.transform.ResourceTransformer;
import org.jboss.as.controller.transform.description.AttributeConverter.DefaultValueAttributeConverter;
import org.jboss.as.controller.transform.description.ResourceTransformationDescriptionBuilder;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
/**
* Resource description for the addressable resource and its alias
*
* /subsystem=infinispan/cache-container=X/cache=Y/component=transaction
* /subsystem=infinispan/cache-container=X/cache=Y/transaction=TRANSACTION
*
* @author Richard Achmatowicz (c) 2011 Red Hat Inc.
*/
public class TransactionResourceDefinition extends ComponentResourceDefinition {
static final PathElement PATH = pathElement("transaction");
static final PathElement LEGACY_PATH = PathElement.pathElement(PATH.getValue(), "TRANSACTION");
enum Attribute implements org.jboss.as.clustering.controller.Attribute {
LOCKING("locking", ModelType.STRING, new ModelNode(LockingMode.PESSIMISTIC.name()), new EnumValidatorBuilder<>(LockingMode.class)),
MODE("mode", ModelType.STRING, new ModelNode(TransactionMode.NONE.name()), new EnumValidatorBuilder<>(TransactionMode.class)),
STOP_TIMEOUT("stop-timeout", ModelType.LONG, new ModelNode(10000L)),
;
private final SimpleAttributeDefinition definition;
Attribute(String name, ModelType type, ModelNode defaultValue) {
this.definition = createBuilder(name, type, defaultValue).build();
}
Attribute(String name, ModelType type, ModelNode defaultValue, ParameterValidatorBuilder validator) {
SimpleAttributeDefinitionBuilder builder = createBuilder(name, type, defaultValue);
this.definition = builder.setValidator(validator.configure(builder).build()).build();
}
private static SimpleAttributeDefinitionBuilder createBuilder(String name, ModelType type, ModelNode defaultValue) {
return new SimpleAttributeDefinitionBuilder(name, type)
.setAllowExpression(true)
.setRequired(false)
.setDefaultValue(defaultValue)
.setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
.setMeasurementUnit((type == ModelType.LONG) ? MeasurementUnit.MILLISECONDS : null)
;
}
@Override
public AttributeDefinition getDefinition() {
return this.definition;
}
}
@SuppressWarnings("deprecation")
static void buildTransformation(ModelVersion version, ResourceTransformationDescriptionBuilder parent) {
ResourceTransformationDescriptionBuilder builder = InfinispanModel.VERSION_4_0_0.requiresTransformation(version) ? parent.addChildRedirection(PATH, LEGACY_PATH, RequiredChildResourceDiscardPolicy.NEVER) : parent.addChildResource(PATH);
List<org.jboss.as.controller.transform.OperationTransformer> addOperationTransformers = new LinkedList<>();
List<org.jboss.as.controller.transform.OperationTransformer> removeOperationTransformers = new LinkedList<>();
Map<String, org.jboss.as.controller.transform.OperationTransformer> readAttributeTransformers = new HashMap<>();
Map<String, org.jboss.as.controller.transform.OperationTransformer> writeAttributeTransformers = new HashMap<>();
Map<String, org.jboss.as.controller.transform.OperationTransformer> undefineAttributeTransformers = new HashMap<>();
if (InfinispanModel.VERSION_3_0_0.requiresTransformation(version)) {
// Convert BATCH -> NONE, and include write-attribute:name=batching
OperationTransformer addTransformer = new OperationTransformer() {
@Override
public ModelNode transformOperation(ModelNode operation) {
if (operation.hasDefined(Attribute.MODE.getName())) {
ModelNode mode = operation.get(Attribute.MODE.getName());
if ((mode.getType() == ModelType.STRING) && (TransactionMode.valueOf(mode.asString()) == TransactionMode.BATCH)) {
mode.set(TransactionMode.NONE.name());
PathAddress address = Operations.getPathAddress(operation);
return Operations.createCompositeOperation(operation, Operations.createWriteAttributeOperation(cacheAddress(address), CacheResourceDefinition.DeprecatedAttribute.BATCHING, new ModelNode(true)));
}
}
return operation;
}
};
addOperationTransformers.add(new SimpleOperationTransformer(addTransformer));
// Additionally include undefine-attribute:name=batching
OperationTransformer removeTransformer = new OperationTransformer() {
@Override
public ModelNode transformOperation(ModelNode operation) {
PathAddress address = Operations.getPathAddress(operation);
return Operations.createCompositeOperation(operation, Operations.createUndefineAttributeOperation(cacheAddress(address), CacheResourceDefinition.DeprecatedAttribute.BATCHING));
}
};
removeOperationTransformers.add(new SimpleOperationTransformer(removeTransformer));
// If read-attribute:name=batching is true, return BATCH, otherwise use result from read-attribute:name=mode
OperationTransformer readAttributeOperationTransformer = new OperationTransformer() {
@Override
public ModelNode transformOperation(ModelNode operation) {
PathAddress address = Operations.getPathAddress(operation);
return Operations.createCompositeOperation(Operations.createReadAttributeOperation(cacheAddress(address), CacheResourceDefinition.DeprecatedAttribute.BATCHING), operation);
}
};
OperationResultTransformer readAttributeResultTransformer = new OperationResultTransformer() {
@Override
public ModelNode transformResult(ModelNode result) {
ModelNode readBatchingResult = result.get(0);
return readBatchingResult.asBoolean() ? new ModelNode(TransactionMode.BATCH.name()) : result.get(1);
}
};
readAttributeTransformers.put(Attribute.MODE.getName(), new SimpleOperationTransformer(readAttributeOperationTransformer, readAttributeResultTransformer));
// Convert BATCH -> NONE, and include write-attribute:name=batching
OperationTransformer writeAttributeTransformer = new OperationTransformer() {
@Override
public ModelNode transformOperation(ModelNode operation) {
ModelNode mode = Operations.getAttributeValue(operation);
boolean batching = (mode.isDefined() && (mode.getType() == ModelType.STRING)) ? (TransactionMode.valueOf(mode.asString()) == TransactionMode.BATCH) : false;
if (batching) {
mode.set(TransactionMode.NONE.name());
}
PathAddress address = Operations.getPathAddress(operation);
return Operations.createCompositeOperation(operation, Operations.createWriteAttributeOperation(cacheAddress(address), CacheResourceDefinition.DeprecatedAttribute.BATCHING, new ModelNode(batching)));
}
};
writeAttributeTransformers.put(Attribute.MODE.getName(), new SimpleOperationTransformer(writeAttributeTransformer));
// Include undefine-attribute:name=batching
OperationTransformer undefineAttributeTransformer = new OperationTransformer() {
@Override
public ModelNode transformOperation(ModelNode operation) {
PathAddress address = Operations.getPathAddress(operation);
return Operations.createCompositeOperation(operation, Operations.createUndefineAttributeOperation(cacheAddress(address), CacheResourceDefinition.DeprecatedAttribute.BATCHING));
}
};
undefineAttributeTransformers.put(Attribute.MODE.getName(), new SimpleOperationTransformer(undefineAttributeTransformer));
// Convert BATCH -> NONE
ResourceTransformer modeTransformer = new ResourceTransformer() {
@Override
public void transformResource(ResourceTransformationContext context, PathAddress address, Resource resource) throws OperationFailedException {
ModelNode model = resource.getModel();
if (model.hasDefined(Attribute.MODE.getName())) {
ModelNode value = model.get(Attribute.MODE.getName());
if ((value.getType() == ModelType.STRING) && (TransactionMode.valueOf(value.asString()) == TransactionMode.BATCH)) {
value.set(TransactionMode.NONE.name());
}
}
context.addTransformedResource(PathAddress.EMPTY_ADDRESS, resource).processChildren(resource);
}
};
builder.setCustomResourceTransformer(modeTransformer);
// change default value of stop-timeout attribute
builder.getAttributeBuilder().setValueConverter(new DefaultValueAttributeConverter(Attribute.STOP_TIMEOUT.getDefinition()), Attribute.STOP_TIMEOUT.getDefinition());
}
buildOperationTransformation(builder, ModelDescriptionConstants.ADD, addOperationTransformers);
buildOperationTransformation(builder, ModelDescriptionConstants.REMOVE, removeOperationTransformers);
buildOperationTransformation(builder, ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION, readAttributeTransformers);
buildOperationTransformation(builder, ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION, writeAttributeTransformers);
buildOperationTransformation(builder, ModelDescriptionConstants.UNDEFINE_ATTRIBUTE_OPERATION, undefineAttributeTransformers);
}
static void buildOperationTransformation(ResourceTransformationDescriptionBuilder builder, String operationName, List<org.jboss.as.controller.transform.OperationTransformer> transformers) {
if (!transformers.isEmpty()) {
builder.addOperationTransformationOverride(operationName).setCustomOperationTransformer(new ChainedOperationTransformer(transformers)).inheritResourceAttributeDefinitions();
}
}
static void buildOperationTransformation(ResourceTransformationDescriptionBuilder builder, String operationName, Map<String, org.jboss.as.controller.transform.OperationTransformer> transformers) {
if (!transformers.isEmpty()) {
builder.addOperationTransformationOverride(operationName).setCustomOperationTransformer(new AttributeOperationTransformer(transformers)).inheritResourceAttributeDefinitions();
}
}
static PathAddress cacheAddress(PathAddress transactionAddress) {
return transactionAddress.subAddress(0, transactionAddress.size() - 1);
}
TransactionResourceDefinition() {
super(PATH);
}
@Override
public void register(ManagementResourceRegistration parentRegistration) {
ManagementResourceRegistration registration = parentRegistration.registerSubModel(this);
parentRegistration.registerAlias(LEGACY_PATH, new SimpleAliasEntry(registration));
ResourceDescriptor descriptor = new ResourceDescriptor(this.getResourceDescriptionResolver()).addAttributes(Attribute.class);
ResourceServiceHandler handler = new SimpleResourceServiceHandler<>(address -> new TransactionBuilder(address.getParent()));
new SimpleResourceRegistration(descriptor, handler).register(registration);
if (registration.isRuntimeOnlyRegistrationValid()) {
new MetricHandler<>(new TransactionMetricExecutor(), TransactionMetric.class).register(registration);
}
}
}