/*
* 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.Arrays;
import org.jboss.as.clustering.infinispan.InfinispanMessages;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
/**
* Common code for handling the following cache configuration elements
* {locking, transaction, eviction, expiration, state-transfer, rehashing, store, file-store, jdbc-store, remote-store}
*
* @author Richard Achmatowicz (c) 2011 Red Hat Inc.
* @author William Burns (c) 2013 Red Hat Inc.
*/
public class CacheConfigOperationHandlers {
static final OperationStepHandler CONTAINER_CONFIGURATIONS_ADD = new CacheConfigAdd();
static final OperationStepHandler CONTAINER_SECURITY_ADD = new CacheConfigAdd();
static final OperationStepHandler LOADER_ADD = new CacheLoaderAdd();
static final OperationStepHandler LOADER_PROPERTY_ADD = new CacheConfigAdd(new AttributeDefinition[]{LoaderPropertyResource.VALUE});
static final OperationStepHandler CLUSTER_LOADER_ADD = new ClusterCacheLoaderAdd();
static final OperationStepHandler STORE_ADD = new CacheStoreAdd();
static final OperationStepHandler STORE_WRITE_BEHIND_ADD = new CacheConfigAdd(StoreWriteBehindResource.ATTRIBUTES);
static final OperationStepHandler FILE_STORE_ADD = new FileCacheStoreAdd();
static final OperationStepHandler STRING_KEYED_JDBC_STORE_ADD = new StringKeyedJDBCCacheStoreAdd();
static final OperationStepHandler REMOTE_STORE_ADD = new RemoteCacheStoreAdd();
static final OperationStepHandler ROCKSDB_STORE_ADD = new RocksDBCacheStoreAdd();
static final OperationStepHandler ROCKSDB_EXPIRATION_ADD = new CacheConfigAdd(RocksDBExpirationConfigurationResource.ATTRIBUTES);
static final OperationStepHandler LEVELDB_COMPRESSION_ADD = new CacheConfigAdd(RocksDBCompressionConfigurationResource.ATTRIBUTES);
static final OperationStepHandler REST_STORE_ADD = new RestCacheStoreAdd();
/**
* Helper class to process adding basic nested cache configuration elements to the cache parent resource.
* When additional configuration is added, services need to be restarted; we restart all of them, for now
* by indicating reload required.
*/
public static class CacheConfigAdd extends AbstractAddStepHandler {
private final AttributeDefinition[] attributes;
CacheConfigAdd() {
this.attributes = new AttributeDefinition[0];
}
CacheConfigAdd(final AttributeDefinition[] attributes) {
this.attributes = attributes;
}
@Override
protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
for (AttributeDefinition attr : attributes) {
attr.validateAndSet(operation, model);
}
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
super.performRuntime(context, operation, model);
// once we add a cache configuration, we need to restart all the services for the changes to take effect
context.reloadRequired();
}
}
/**
* Base class for adding cache loaders.
*
* This class needs to do the following:
* - check that its parent has no existing defined cache loader
* - process its model attributes
* - create any child resources required for the loader resource, such as a set of properties
*
*/
abstract static class AbstractCacheLoaderAdd extends AbstractAddStepHandler {
protected final AttributeDefinition[] attributes;
AbstractCacheLoaderAdd() {
this(BaseLoaderConfigurationResource.BASE_LOADER_PARAMETERS);
}
AbstractCacheLoaderAdd(AttributeDefinition[] attributes) {
this(attributes, false);
}
/**
* @param attributes The attributes or additional attributes to use
* @param includeCommonLoaderAttributes Loader implementations should provide false for this
*/
AbstractCacheLoaderAdd(AttributeDefinition[] attributes, boolean includeCommonLoaderAttributes) {
if (includeCommonLoaderAttributes) {
this.attributes = concat(BaseLoaderConfigurationResource.BASE_LOADER_PARAMETERS, attributes);
}
else {
this.attributes = attributes;
}
}
@Override
protected void populateModel(OperationContext context, ModelNode operation, Resource resource) throws OperationFailedException {
final ModelNode model = resource.getModel();
// Process attributes
for(final AttributeDefinition attribute : attributes) {
// we use PROPERTIES only to allow the user to pass in a list of properties on store add commands
// don't copy these into the model
if (attribute.getName().equals(BaseStoreConfigurationResource.PROPERTIES.getName()))
continue ;
attribute.validateAndSet(operation, model);
}
// Process type specific properties if required
populateSubclassModel(context, operation, model);
// The cache config parameters <property name=>value</property>
if(operation.hasDefined(ModelKeys.PROPERTIES)) {
// CLI will be a list where there is N elements each with a single map
// RHQ will be a list with 1 element where each element is a map with N elements
// Thus we have to iterate this way instead of just doing asPropertyList for example from the PROPERTIES
// ModelNode returned from the operation
for(ModelNode node : operation.get(ModelKeys.PROPERTIES).asList()) {
for (Property property : node.asPropertyList()) {
// create a new property=name resource
final Resource param = context.createResource(PathAddress.pathAddress(PathElement.pathElement(ModelKeys.PROPERTY, property.getName())));
final ModelNode value = property.getValue();
if(! value.isDefined()) {
throw InfinispanMessages.MESSAGES.propertyValueNotDefined(property.getName());
}
ModelNode holder = new ModelNode();
holder.get(LoaderPropertyResource.VALUE.getName()).set(value);
// set the value of the property
LoaderPropertyResource.VALUE.validateAndSet(holder, param.getModel());
}
}
}
}
void populateSubclassModel(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
// this abstract method is called when populateModel() is called in the base class
for(final AttributeDefinition attribute : attributes) {
attribute.validateAndSet(operation, model);
}
}
@Override
protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
// do nothing
}
}
/**
* Add a basic cache loader to a cache.
*/
private static class CacheLoaderAdd extends AbstractCacheLoaderAdd {
CacheLoaderAdd() {
super(LoaderConfigurationResource.LOADER_ATTRIBUTES, true);
}
}
/**
* Add a cluster cache loader to a cache.
*/
private static class ClusterCacheLoaderAdd extends AbstractCacheLoaderAdd {
ClusterCacheLoaderAdd() {
super(ClusterLoaderConfigurationResource.ATTRIBUTES, true);
}
}
/**
* Base class for adding cache stores.
*
* This class needs to do the following:
* - check that its parent has no existing defined cache store
* - process its model attributes
* - create any child resources required for the store resource, such as a set of properties
*
*/
abstract static class AbstractCacheStoreAdd extends AbstractCacheLoaderAdd {
AbstractCacheStoreAdd() {
super(BaseStoreConfigurationResource.BASE_STORE_PARAMETERS);
}
AbstractCacheStoreAdd(AttributeDefinition[] attributes) {
super(concat(BaseStoreConfigurationResource.BASE_STORE_PARAMETERS, attributes));
}
}
private static AttributeDefinition[] concat(AttributeDefinition[] A, AttributeDefinition[] B) {
int aLen = A.length;
int bLen = B.length;
AttributeDefinition[] C;
if (bLen > 0) {
C = Arrays.copyOf(A, aLen + bLen);
System.arraycopy(B, 0, C, aLen, bLen);
}
else {
C = A;
}
return C;
}
/**
* Add a basic cache store to a cache.
*/
private static class CacheStoreAdd extends AbstractCacheStoreAdd {
CacheStoreAdd() {
super(StoreConfigurationResource.STORE_ATTRIBUTES);
}
}
/**
* Add a file cache store to a cache.
*/
private static class FileCacheStoreAdd extends AbstractCacheStoreAdd {
FileCacheStoreAdd() {
super(FileStoreResource.ATTRIBUTES);
}
}
private static class JDBCCacheStoreAdd extends AbstractCacheStoreAdd {
JDBCCacheStoreAdd() {
super(BaseJDBCStoreConfigurationResource.COMMON_JDBC_STORE_ATTRIBUTES);
}
}
private static class StringKeyedJDBCCacheStoreAdd extends JDBCCacheStoreAdd {
private final AttributeDefinition[] additionalAttributes;
StringKeyedJDBCCacheStoreAdd() {
this.additionalAttributes = StringKeyedJDBCStoreResource.ATTRIBUTES;
}
@Override
protected void populateSubclassModel(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
// this abstract method is called when populateModel() is called in the base class
super.populateSubclassModel(context, operation, model);
for(final AttributeDefinition attribute : additionalAttributes) {
attribute.validateAndSet(operation, model);
}
// now check for string-keyed-table passed as optional parameter, in order to create the resource
// if (operation.get("string-keyed-table").isDefined()) {
// ModelNode stringTable = operation.get("string-keyed-table") ;
// // process this table DMR description
// }
}
}
private static class RemoteCacheStoreAdd extends AbstractCacheStoreAdd {
RemoteCacheStoreAdd() {
super(RemoteStoreConfigurationResource.REMOTE_STORE_ATTRIBUTES);
}
}
private static class RocksDBCacheStoreAdd extends AbstractCacheStoreAdd {
RocksDBCacheStoreAdd() {
super(RocksDBStoreConfigurationResource.ROCKSDB_STORE_ATTRIBUTES);
}
}
private static class RestCacheStoreAdd extends AbstractCacheStoreAdd {
RestCacheStoreAdd() {
super(RestStoreConfigurationResource.REST_STORE_ATTRIBUTES);
}
}
}