diff --git a/core/src/main/java/org/infinispan/globalstate/impl/OverlayLocalConfigurationStorage.java b/core/src/main/java/org/infinispan/globalstate/impl/OverlayLocalConfigurationStorage.java index 0010a344e284..3cd65d87b289 100644 --- a/core/src/main/java/org/infinispan/globalstate/impl/OverlayLocalConfigurationStorage.java +++ b/core/src/main/java/org/infinispan/globalstate/impl/OverlayLocalConfigurationStorage.java @@ -34,9 +34,6 @@ public class OverlayLocalConfigurationStorage extends VolatileLocalConfigurationStorage { private ConcurrentHashSet persistentCaches = new ConcurrentHashSet<>(); - public OverlayLocalConfigurationStorage() { - } - @Override public void validateFlags(EnumSet flags) { if (flags.contains(CacheContainerAdmin.AdminFlag.PERMANENT) && !globalConfiguration.globalState().enabled()) diff --git a/core/src/main/java/org/infinispan/globalstate/impl/VolatileLocalConfigurationStorage.java b/core/src/main/java/org/infinispan/globalstate/impl/VolatileLocalConfigurationStorage.java index 05a42d88cc03..6c4b8a95ec29 100644 --- a/core/src/main/java/org/infinispan/globalstate/impl/VolatileLocalConfigurationStorage.java +++ b/core/src/main/java/org/infinispan/globalstate/impl/VolatileLocalConfigurationStorage.java @@ -38,9 +38,6 @@ public class VolatileLocalConfigurationStorage implements LocalConfigurationStor protected ParserRegistry parserRegistry; protected GlobalConfiguration globalConfiguration; - public VolatileLocalConfigurationStorage() { - } - public void initialize(EmbeddedCacheManager cacheManager) { this.globalConfiguration = cacheManager.getCacheManagerConfiguration(); this.cacheManager = cacheManager; diff --git a/documentation/src/main/asciidoc/user_guide/cache_manager.adoc b/documentation/src/main/asciidoc/user_guide/cache_manager.adoc index 0b5e550945a6..1f2f15e49ee6 100644 --- a/documentation/src/main/asciidoc/user_guide/cache_manager.adoc +++ b/documentation/src/main/asciidoc/user_guide/cache_manager.adoc @@ -90,7 +90,7 @@ In order for the above to work, global state must be enabled and a suitable conf The available configuration stores are: - `VOLATILE`: as the name implies, this configuration storage does not support +PERMANENT+ caches. -- `OVERLAY`: this stores configurations in the global state persistent path in a file named _caches.xml_. +- `OVERLAY`: this stores configurations in the global shared state persistent path in a file named _caches.xml_. - `MANAGED`: this is only supported in server deployments, and will store +PERMANENT+ caches in the server model. - `CUSTOM`: a custom configuration store. diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/InfinispanLogger.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/InfinispanLogger.java index 6faa949b9dac..70f85e6f7dcc 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/InfinispanLogger.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/InfinispanLogger.java @@ -194,4 +194,8 @@ public interface InfinispanLogger extends BasicLogger { @LogMessage(level = ERROR) @Message(id = 20, value = "Waiting for deployment of custom EntryMergePolicy (%s) timed out. Please check if this EntryMergePolicy is really present.") void loadingCustomMergePolicyTimeout(String className); + + @LogMessage(level = WARN) + @Message(id = 21, value = "Managed configuration storage is currently unsupported in domain mode. Please use Overlay storage.") + void managedConfigurationUnavailableInDomainMode(); } diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/InfinispanMessages.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/InfinispanMessages.java index 2b7da1487f21..648acf9bde5c 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/InfinispanMessages.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/InfinispanMessages.java @@ -278,4 +278,19 @@ public interface InfinispanMessages { @Message(id = 125, value = "%s is not a valid EntryMergePolicy") IllegalArgumentException invalidEntryMergePolicy(@Cause Throwable cause, String mergePolicy); + + @Message(id = 126, value = "Cannot create cache '%s'") + CacheConfigurationException cannotCreateCache(@Cause Throwable cause, String name); + + @Message(id = 127, value = "Cannot remove cache '%s'") + CacheConfigurationException cannotRemoveCache(@Cause Throwable e, String name); + + @Message(id = 128, value = "Cache '%s' does not exist") + CacheConfigurationException nonExistingCache(String name); + + @Message(id = 129, value = "Cannot create non-permanent cache '%s'") + CacheConfigurationException cannotCreateNonPermamentCache(String name); + + @Message(id = 130, value = "Cannot create cache '%s' since template '%s' does not exist") + CacheConfigurationException nonExistingTemplate(String name, String template); } diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/Attribute.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/Attribute.java index 82b59279f47f..3cd01323de73 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/Attribute.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/Attribute.java @@ -64,6 +64,8 @@ public enum Attribute { CLUSTER(ModelKeys.CLUSTER), CONCURRENCY_LEVEL(ModelKeys.CONCURRENCY_LEVEL), CONFIGURATION(ModelKeys.CONFIGURATION), + CONFIGURATION_STORAGE(ModelKeys.CONFIGURATION_STORAGE), + CONFIGURATION_STORAGE_CLASS(ModelKeys.CONFIGURATION_STORAGE_CLASS), CONNECTION_TIMEOUT(ModelKeys.CONNECTION_TIMEOUT), CREATE_ON_START(ModelKeys.CREATE_ON_START), DATASOURCE(ModelKeys.DATASOURCE), diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/CacheContainerAddHandler.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/CacheContainerAddHandler.java index 34341be7cd13..e490faa94be5 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/CacheContainerAddHandler.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/CacheContainerAddHandler.java @@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit; import org.infinispan.Cache; +import org.infinispan.globalstate.ConfigurationStorage; import org.infinispan.server.commons.controller.ReloadRequiredAddStepHandler; import org.infinispan.server.commons.dmr.ModelNodes; import org.infinispan.server.commons.naming.BinderServiceBuilder; @@ -45,6 +46,7 @@ import org.infinispan.server.jgroups.spi.service.ChannelServiceNameFactory; import org.infinispan.server.jgroups.spi.service.ProtocolStackServiceName; import org.infinispan.server.jgroups.subsystem.JGroupsBindingFactory; +import org.jboss.as.clustering.infinispan.InfinispanLogger; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; @@ -147,6 +149,23 @@ static void installRuntimeServices(OperationContext context, ModelNode operation } else { globalStateBuilder.setTemporaryPath(defaultPersistentLocation).setTemporaryRelativeTo(ServerEnvironment.SERVER_TEMP_DIR); } + ConfigurationStorage configurationStorage = ConfigurationStorage.valueOf(GlobalStateResource.CONFIGURATION_STORAGE.resolveModelAttribute(context, globalState).asString()); + globalStateBuilder.setConfigurationStorage(configurationStorage); + if (configurationStorage.equals(ConfigurationStorage.MANAGED)) { + switch (context.getProcessType()) { + case STANDALONE_SERVER: + globalStateBuilder.setConfigurationStorageClass(StandaloneServerLocalConfigurationStorage.class.getName()); + break; + case DOMAIN_SERVER: + InfinispanLogger.ROOT_LOGGER.managedConfigurationUnavailableInDomainMode(); + break; + default: + // No need + break; + } + } else if (configurationStorage.equals(ConfigurationStorage.CUSTOM)) { + globalStateBuilder.setConfigurationStorageClass(GlobalStateResource.CONFIGURATION_STORAGE_CLASS.resolveModelAttribute(context, globalState).asString()); + } } if (model.hasDefined(ModelKeys.SECURITY) && model.get(ModelKeys.SECURITY).hasDefined(ModelKeys.SECURITY_NAME)) { diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/CacheContainerConfigurationBuilder.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/CacheContainerConfigurationBuilder.java index 43d63f98cb6e..8a442f9bab06 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/CacheContainerConfigurationBuilder.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/CacheContainerConfigurationBuilder.java @@ -26,6 +26,7 @@ import java.util.Map.Entry; import java.util.ServiceLoader; import java.util.Set; +import java.util.concurrent.Executors; import javax.management.MBeanServer; @@ -38,6 +39,7 @@ import org.infinispan.configuration.global.ShutdownHookBehavior; import org.infinispan.configuration.global.ThreadPoolConfiguration; import org.infinispan.configuration.internal.PrivateGlobalConfigurationBuilder; +import org.infinispan.globalstate.LocalConfigurationStorage; import org.infinispan.marshall.core.Ids; import org.infinispan.security.AuditLogger; import org.infinispan.security.PrincipalRoleMapper; @@ -57,6 +59,8 @@ import org.jboss.as.clustering.infinispan.subsystem.EmbeddedCacheManagerConfigurationService.AuthorizationConfiguration; import org.jboss.as.clustering.infinispan.subsystem.EmbeddedCacheManagerConfigurationService.GlobalStateLocationConfiguration; import org.jboss.as.clustering.infinispan.subsystem.EmbeddedCacheManagerConfigurationService.TransportConfiguration; +import org.jboss.as.controller.ModelController; +import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.services.path.PathManager; import org.jboss.as.controller.services.path.PathManagerService; import org.jboss.as.jmx.MBeanServerService; @@ -97,6 +101,7 @@ public class CacheContainerConfigurationBuilder implements Builder transportThreadPool = new InjectedValue<>(); private final InjectedValue replicationQueueThreadPool = new InjectedValue<>(); private final InjectedValue pathManager = new InjectedValue<>(); + private final InjectedValue modelController = new InjectedValue<>(); public CacheContainerConfigurationBuilder(String name) { this.name = name; @@ -110,6 +115,7 @@ public ServiceName getServiceName() { @Override public ServiceBuilder build(ServiceTarget target) { ServiceBuilder builder = target.addService(this.getServiceName(), new ValueService<>(this)) + .addDependency(Services.JBOSS_SERVER_CONTROLLER, ModelController.class, this.modelController) .addDependency(Services.JBOSS_SERVICE_MODULE_LOADER, ModuleLoader.class, this.loader) .addDependency(MBeanServerService.SERVICE_NAME, MBeanServer.class, this.server) .addDependency(ThreadPoolResource.ASYNC_OPERATIONS.getServiceName(this.name), ThreadPoolConfiguration.class, this.asyncOperationsThreadPool) @@ -226,6 +232,22 @@ public GlobalConfiguration getValue() { statePersistenceBuilder.persistentLocation(persistentLocation); String temporaryLocation = pathManager.getValue().resolveRelativePathEntry(statePersistence.getPersistencePath(), statePersistence.getPersistenceRelativeTo()); statePersistenceBuilder.temporaryLocation(temporaryLocation); + statePersistenceBuilder.configurationStorage(statePersistence.getConfigurationStorage()); + // If the LocalConfigurationStorage is server-aware, apply some context + String configurationStorageClass = statePersistence.getConfigurationStorageClass(); + if (configurationStorageClass != null) { + try { + LocalConfigurationStorage localConfigurationStorage = Class.forName(configurationStorageClass, true, loader).asSubclass(LocalConfigurationStorage.class).newInstance(); + if (localConfigurationStorage != null && localConfigurationStorage instanceof ServerLocalConfigurationStorage) { + ServerLocalConfigurationStorage serverLocalConfigurationManager = (ServerLocalConfigurationStorage)localConfigurationStorage; + serverLocalConfigurationManager.setRootPath(PathAddress.pathAddress(InfinispanExtension.SUBSYSTEM_PATH).append("cache-container", name)); + serverLocalConfigurationManager.setModelControllerClient(modelController.getValue().createClient(Executors.newCachedThreadPool())); + } + statePersistenceBuilder.configurationStorageSupplier(() -> localConfigurationStorage); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } } builder.asyncThreadPool().read(this.asyncOperationsThreadPool.getValue()); diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/Element.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/Element.java index e1602db8efb7..1bb005783bdd 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/Element.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/Element.java @@ -52,6 +52,7 @@ public enum Element { COMPRESSION(ModelKeys.COMPRESSION), COMMON_NAME_ROLE_MAPPER(ModelKeys.COMMON_NAME_ROLE_MAPPER), CLUSTER_ROLE_MAPPER(ModelKeys.CLUSTER_ROLE_MAPPER), + CUSTOM_CONFIGURATION_STORAGE(ModelKeys.CUSTOM_CONFIGURATION_STORAGE), CUSTOM_ROLE_MAPPER(ModelKeys.CUSTOM_ROLE_MAPPER), DATA_COLUMN(ModelKeys.DATA_COLUMN), DIGEST(ModelKeys.DIGEST), @@ -64,6 +65,7 @@ public enum Element { EXTERNAL(ModelKeys.EXTERNAL), FILE_STORE(ModelKeys.FILE_STORE), GLOBAL_STATE(ModelKeys.GLOBAL_STATE), + MANAGED_CONFIGURATION_STORAGE(ModelKeys.MANAGED_CONFIGURATION_STORAGE), MODULES(ModelKeys.MODULES), MODULE(ModelKeys.MODULE), ID_COLUMN(ModelKeys.ID_COLUMN), @@ -85,6 +87,7 @@ public enum Element { MEMORY(ModelKeys.MEMORY), OBJECT("object"), OFF_HEAP("off-heap"), + OVERLAY_CONFIGURATION_STORAGE(ModelKeys.OVERLAY_CONFIGURATION_STORAGE), PARTITION_HANDLING(ModelKeys.PARTITION_HANDLING), PERSISTENCE(ModelKeys.PERSISTENCE), PERSISTENT_LOCATION(ModelKeys.PERSISTENT_LOCATION), @@ -108,6 +111,7 @@ public enum Element { TRANSACTION(ModelKeys.TRANSACTION), TRANSPORT(ModelKeys.TRANSPORT), VALUE(ModelKeys.VALUE), + VOLATILE_CONFIGURATION_STORAGE(ModelKeys.VOLATILE_CONFIGURATION_STORAGE), WRITE_BEHIND(ModelKeys.WRITE_BEHIND), ASYNC_OPERATIONS_THREAD_POOL(ModelKeys.ASYNC_OPERATIONS_THREAD_POOL), diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/EmbeddedCacheManagerConfigurationService.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/EmbeddedCacheManagerConfigurationService.java index 48866f11589c..fb55bcb634f8 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/EmbeddedCacheManagerConfigurationService.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/EmbeddedCacheManagerConfigurationService.java @@ -26,6 +26,7 @@ import javax.management.MBeanServer; +import org.infinispan.globalstate.ConfigurationStorage; import org.infinispan.server.jgroups.spi.ChannelFactory; import org.jboss.modules.ModuleLoader; import org.jgroups.JChannel; @@ -55,6 +56,8 @@ interface GlobalStateLocationConfiguration { String getPersistenceRelativeTo(); String getTemporaryPath(); String getTemporaryRelativeTo(); + ConfigurationStorage getConfigurationStorage(); + String getConfigurationStorageClass(); } interface Dependencies { diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/GlobalStateLocationConfigurationBuilder.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/GlobalStateLocationConfigurationBuilder.java index 6a0e6c609047..39b2a94ce911 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/GlobalStateLocationConfigurationBuilder.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/GlobalStateLocationConfigurationBuilder.java @@ -1,5 +1,6 @@ package org.jboss.as.clustering.infinispan.subsystem; +import org.infinispan.globalstate.ConfigurationStorage; import org.jboss.as.clustering.infinispan.subsystem.EmbeddedCacheManagerConfigurationService.GlobalStateLocationConfiguration; import org.jboss.msc.value.Value; @@ -14,6 +15,8 @@ public class GlobalStateLocationConfigurationBuilder implements Value(ConfigurationStorage.class, false, true)) + .setDefaultValue(new ModelNode().set(ConfigurationStorage.OVERLAY.toString())) + .build(); + + static final SimpleAttributeDefinition CONFIGURATION_STORAGE_CLASS = + new SimpleAttributeDefinitionBuilder(ModelKeys.CONFIGURATION_STORAGE_CLASS, ModelType.STRING, true) + .setXmlName(Attribute.CONFIGURATION_STORAGE_CLASS.getLocalName()) + .setAllowExpression(true) + .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES) + .build(); + + static final AttributeDefinition[] ATTRIBUTES = { PERSISTENT_LOCATION_PATH, TEMPORARY_STATE_PATH, CONFIGURATION_STORAGE, CONFIGURATION_STORAGE_CLASS}; GlobalStateResource() { super(GLOBAL_STATE_PATH, diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/InfinispanSubsystemXMLReader.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/InfinispanSubsystemXMLReader.java index 38c7ad3348f8..7cee1dc2d774 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/InfinispanSubsystemXMLReader.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/InfinispanSubsystemXMLReader.java @@ -40,6 +40,7 @@ import org.infinispan.eviction.EvictionStrategy; import org.infinispan.eviction.EvictionType; +import org.infinispan.globalstate.ConfigurationStorage; import org.infinispan.partitionhandling.PartitionHandling; import org.infinispan.security.impl.ClusterRoleMapper; import org.infinispan.security.impl.CommonNameRoleMapper; @@ -527,6 +528,7 @@ private void parseCounterBound(XMLExtendedStreamReader reader, Element element, private void parseGlobalState(XMLExtendedStreamReader reader, PathAddress containerAddress, Map operations) throws XMLStreamException { + ConfigurationStorage storage = null; PathAddress globalStateAddress = containerAddress.append(ModelKeys.GLOBAL_STATE, ModelKeys.GLOBAL_STATE_NAME); ModelNode globalState = Util.createAddOperation(globalStateAddress); while (reader.hasNext() && (reader.nextTag() != XMLStreamConstants.END_ELEMENT)) { @@ -540,11 +542,44 @@ private void parseGlobalState(XMLExtendedStreamReader reader, PathAddress contai parseGlobalStatePath(reader, globalState, GlobalStateResource.TEMPORARY_STATE_PATH); break; } + case VOLATILE_CONFIGURATION_STORAGE: { + if (storage != null) { + throw ParseUtils.unexpectedElement(reader); + } + storage = ConfigurationStorage.VOLATILE; + break; + } + case OVERLAY_CONFIGURATION_STORAGE: { + if (storage != null) { + throw ParseUtils.unexpectedElement(reader); + } + storage = ConfigurationStorage.OVERLAY; + break; + } + case MANAGED_CONFIGURATION_STORAGE: { + if (storage != null) { + throw ParseUtils.unexpectedElement(reader); + } + storage = ConfigurationStorage.MANAGED; + break; + } + case CUSTOM_CONFIGURATION_STORAGE: { + if (storage != null) { + throw ParseUtils.unexpectedElement(reader); + } + storage = ConfigurationStorage.CUSTOM; + String klass = ParseUtils.readStringAttributeElement(reader, Attribute.CLASS.getLocalName()); + GlobalStateResource.CONFIGURATION_STORAGE_CLASS.parseAndSetParameter(klass, globalState, reader); + break; + } default: { throw ParseUtils.unexpectedElement(reader); } } } + if (storage != null) { + GlobalStateResource.CONFIGURATION_STORAGE.parseAndSetParameter(storage.name(), globalState, reader); + } operations.put(globalStateAddress, globalState); } diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/InfinispanSubsystemXMLWriter.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/InfinispanSubsystemXMLWriter.java index 7be1a9a5e130..1955fff568c4 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/InfinispanSubsystemXMLWriter.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/InfinispanSubsystemXMLWriter.java @@ -29,6 +29,7 @@ import javax.xml.stream.XMLStreamException; +import org.infinispan.globalstate.ConfigurationStorage; import org.infinispan.security.impl.ClusterRoleMapper; import org.infinispan.security.impl.CommonNameRoleMapper; import org.infinispan.security.impl.IdentityRoleMapper; @@ -142,6 +143,25 @@ public void writeContent(XMLExtendedStreamWriter writer, SubsystemMarshallingCon ModelNode globalState = container.get(ModelKeys.GLOBAL_STATE, ModelKeys.GLOBAL_STATE_NAME); writeStatePathElement(Element.PERSISTENT_LOCATION, ModelKeys.PERSISTENT_LOCATION, writer, globalState); writeStatePathElement(Element.TEMPORARY_LOCATION, ModelKeys.TEMPORARY_LOCATION, writer, globalState); + if (globalState.hasDefined(ModelKeys.CONFIGURATION_STORAGE)) { + ConfigurationStorage configurationStorage = ConfigurationStorage.valueOf(globalState.get(ModelKeys.CONFIGURATION_STORAGE).asString()); + switch (configurationStorage) { + case VOLATILE: + writer.writeEmptyElement(Element.VOLATILE_CONFIGURATION_STORAGE.getLocalName()); + break; + case OVERLAY: + writer.writeEmptyElement(Element.OVERLAY_CONFIGURATION_STORAGE.getLocalName()); + break; + case MANAGED: + writer.writeEmptyElement(Element.MANAGED_CONFIGURATION_STORAGE.getLocalName()); + break; + case CUSTOM: + writer.writeStartElement(Element.CUSTOM_CONFIGURATION_STORAGE.getLocalName()); + writer.writeAttribute(Attribute.CLASS.getLocalName(), globalState.get(ModelKeys.CONFIGURATION_STORAGE_CLASS).asString()); + writer.writeEndElement(); + break; + } + } writer.writeEndElement(); } diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/ModelKeys.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/ModelKeys.java index 0c5368dc9af5..146af74bd79b 100644 --- a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/ModelKeys.java +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/ModelKeys.java @@ -77,6 +77,8 @@ public class ModelKeys { static final String COMPRESSION_NAME = "COMPRESSION"; static final String CONCURRENCY_LEVEL = "concurrency-level"; static final String CONFIGURATION = "configuration"; + static final String CONFIGURATION_STORAGE = "configuration-storage"; + static final String CONFIGURATION_STORAGE_CLASS = "configuration-storage-class"; static final String CONFIGURATIONS = "configurations"; static final String CONFIGURATIONS_NAME = "CONFIGURATIONS"; static final String CONNECTION_POOL = "connection-pool"; @@ -84,6 +86,7 @@ public class ModelKeys { static final String COUNTERS = "counters"; static final String COUNTERS_NAME = "COUNTERS"; static final String CREATE_ON_START = "create-on-start"; + static final String CUSTOM_CONFIGURATION_STORAGE = "custom-configuration-storage"; static final String CUSTOM_ROLE_MAPPER = "custom-role-mapper"; static final String DATA_COLUMN = "data-column"; static final String DATASOURCE = "datasource"; @@ -157,6 +160,7 @@ public class ModelKeys { static final String LOCKING_NAME = "LOCKING"; static final String LOWER_BOUND = "lower-bound"; static final String MACHINE = "machine"; + static final String MANAGED_CONFIGURATION_STORAGE = "managed-configuration-storage"; static final String MAPPER = "mapper"; static final String MARSHALLER = "marshaller"; static final String MAX_BATCH_SIZE = "max-batch-size"; @@ -184,6 +188,7 @@ public class ModelKeys { static final String OBJECT_NAME = "OBJECT"; static final String OFF_HEAP_NAME = "OFF-HEAP"; static final String OUTBOUND_SOCKET_BINDING = "outbound-socket-binding"; + static final String OVERLAY_CONFIGURATION_STORAGE = "overlay-configuration-storage"; static final String OWNERS = "owners"; static final String PARTITION_HANDLING = "partition-handling"; static final String PARTITION_HANDLING_NAME = "PARTITION_HANDLING"; @@ -277,6 +282,7 @@ public class ModelKeys { static final String USERNAME = "username"; static final String UPPER_BOUND = "upper-bound"; static final String VALUE = "value"; + static final String VOLATILE_CONFIGURATION_STORAGE = "volatile-configuration-storage"; static final String WAIT = "wait"; static final String WAIT_TIME = "wait-time"; static final String WHEN_SPLIT = "when-split"; diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/ServerLocalConfigurationStorage.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/ServerLocalConfigurationStorage.java new file mode 100644 index 000000000000..ed8624128633 --- /dev/null +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/ServerLocalConfigurationStorage.java @@ -0,0 +1,17 @@ +package org.jboss.as.clustering.infinispan.subsystem; + +import org.infinispan.globalstate.LocalConfigurationStorage; +import org.jboss.as.controller.PathAddress; +import org.jboss.as.controller.client.ModelControllerClient; + +/** + * @author Tristan Tarrant + * @since 9.2 + */ + +public interface ServerLocalConfigurationStorage extends LocalConfigurationStorage { + + void setRootPath(PathAddress rootPath); + + void setModelControllerClient(ModelControllerClient modelControllerClient); +} diff --git a/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/StandaloneServerLocalConfigurationStorage.java b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/StandaloneServerLocalConfigurationStorage.java new file mode 100644 index 000000000000..cac91632ed9a --- /dev/null +++ b/server/integration/infinispan/src/main/java/org/jboss/as/clustering/infinispan/subsystem/StandaloneServerLocalConfigurationStorage.java @@ -0,0 +1,141 @@ +package org.jboss.as.clustering.infinispan.subsystem; + +import static org.jboss.as.controller.client.helpers.ClientConstants.ADD; +import static org.jboss.as.controller.client.helpers.ClientConstants.NAME; +import static org.jboss.as.controller.client.helpers.ClientConstants.OP; +import static org.jboss.as.controller.client.helpers.ClientConstants.OP_ADDR; +import static org.jboss.as.controller.client.helpers.ClientConstants.OUTCOME; +import static org.jboss.as.controller.client.helpers.ClientConstants.READ_ATTRIBUTE_OPERATION; +import static org.jboss.as.controller.client.helpers.ClientConstants.REMOVE_OPERATION; +import static org.jboss.as.controller.client.helpers.ClientConstants.SUCCESS; + +import java.io.IOException; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Map; + +import org.infinispan.commons.api.CacheContainerAdmin; +import org.infinispan.configuration.cache.Configuration; +import org.infinispan.globalstate.LocalConfigurationStorage; +import org.infinispan.manager.EmbeddedCacheManager; +import org.jboss.as.clustering.infinispan.InfinispanMessages; +import org.jboss.as.controller.PathAddress; +import org.jboss.as.controller.client.ModelControllerClient; +import org.jboss.dmr.ModelNode; + +/** + * A {@link LocalConfigurationStorage} which saves {@link org.infinispan.commons.api.CacheContainerAdmin.AdminFlag#PERMANENT} + * changes to the server configuration model in standalone mode. + * + * @author Tristan Tarrant + * @since 9.2 + */ + +public class StandaloneServerLocalConfigurationStorage implements ServerLocalConfigurationStorage { + private static final String[] CACHE_MODES = {"local-cache", "invalidation-cache", "replicated-cache", "distributed-cache", "scattered-cache"}; + private ModelControllerClient modelControllerClient; + private PathAddress rootPath; + + @Override + public void initialize(EmbeddedCacheManager embeddedCacheManager) { + // NO-OP + } + + @Override + public void validateFlags(EnumSet flags) { + } + + @Override + public void createCache(String name, String template, Configuration configuration, EnumSet flags) { + if (!flags.contains(CacheContainerAdmin.AdminFlag.PERMANENT)) { + throw InfinispanMessages.MESSAGES.cannotCreateNonPermamentCache(name); + } + + if (template == null) { + throw InfinispanMessages.MESSAGES.nonExistingTemplate(name, template); + } + + String cacheMode = findCacheMode(template, true); + + if (cacheMode == null) { + throw InfinispanMessages.MESSAGES.nonExistingTemplate(name, template); + } + + final ModelNode cacheAddOp = new ModelNode(); + cacheAddOp.get(OP).set(ADD); + cacheAddOp.get(OP_ADDR).set(rootPath.append(cacheMode, name).toModelNode()); + cacheAddOp.get("configuration").set(template); + + try { + ModelNode resp = modelControllerClient.execute(cacheAddOp); + if (!SUCCESS.equals(resp.get(OUTCOME).asString())) { + throw InfinispanMessages.MESSAGES.cannotCreateCache(null, name); + } + } catch (IOException e) { + throw InfinispanMessages.MESSAGES.cannotCreateCache(e, name); + } + } + + @Override + public void removeCache(String name, EnumSet flags) { + String cacheMode = findCacheMode(name, false); + + if (cacheMode == null) { + throw InfinispanMessages.MESSAGES.nonExistingCache(name); + } + + final ModelNode cacheRemoveOp = new ModelNode(); + cacheRemoveOp.get(OP).set(REMOVE_OPERATION); + cacheRemoveOp.get(OP_ADDR).set(rootPath.append(cacheMode, name).toModelNode()); + + try { + ModelNode resp = modelControllerClient.execute(cacheRemoveOp); + if (!SUCCESS.equals(resp.get(OUTCOME).asString())) { + throw InfinispanMessages.MESSAGES.cannotRemoveCache(null, name); + } + } catch (IOException e) { + throw InfinispanMessages.MESSAGES.cannotRemoveCache(e, name); + } + } + + private String findCacheMode(String name, boolean configuration) { + for (String cacheMode : CACHE_MODES) { + PathAddress address = rootPath; + if (configuration) { + address = address.append(CacheContainerConfigurationsResource.PATH).append(cacheMode + "-configuration", name); + } else { + address = address.append(cacheMode, name); + } + ModelNode op = new ModelNode(); + op.get(OP).set(READ_ATTRIBUTE_OPERATION); + op.get(OP_ADDR).set(address.toModelNode()); + op.get(NAME).set(configuration ? "template" : "configuration"); + try { + ModelNode resp = modelControllerClient.execute(op); + if (SUCCESS.equals(resp.get(OUTCOME).asString())) { + return cacheMode; + } + } catch (IOException e) { + // Ignore and try the next + } + } + // Nothing found + return null; + } + + @Override + public Map loadAll() { + // No need, persistent configurations will already have been loaded + return Collections.emptyMap(); + } + + @Override + public void setRootPath(PathAddress rootPath) { + this.rootPath = rootPath; + } + + @Override + public void setModelControllerClient(ModelControllerClient modelControllerClient) { + this.modelControllerClient = modelControllerClient; + } +} diff --git a/server/integration/infinispan/src/main/resources/org/jboss/as/clustering/infinispan/subsystem/LocalDescriptions.properties b/server/integration/infinispan/src/main/resources/org/jboss/as/clustering/infinispan/subsystem/LocalDescriptions.properties index 23bea10016d4..09efe9d33862 100644 --- a/server/integration/infinispan/src/main/resources/org/jboss/as/clustering/infinispan/subsystem/LocalDescriptions.properties +++ b/server/integration/infinispan/src/main/resources/org/jboss/as/clustering/infinispan/subsystem/LocalDescriptions.properties @@ -577,6 +577,8 @@ datagrid-infinispan.cache-container.global-state.temporary-location.relative-to= datagrid-infinispan.cache-container.global-state.persistent-location=The filesystem location where persistent state data will be stored datagrid-infinispan.cache-container.global-state.persistent-location.path=The path portion of the persistent state location datagrid-infinispan.cache-container.global-state.persistent-location.relative-to=The relative-to portion of the persistent state location +datagrid-infinispan.cache-container.global-state.configuration-storage=The type of configuration storage for runtime-defined caches +datagrid-infinispan.cache-container.global-state.configuration-storage-class=The class for a custom configuration storage datagrid-infinispan.cache-container.security=The security configuration of a cache container datagrid-infinispan.cache-container.security.add=Add security configuration to a cache container diff --git a/server/integration/infinispan/src/main/resources/schema/jboss-infinispan-core_9_2.xsd b/server/integration/infinispan/src/main/resources/schema/jboss-infinispan-core_9_2.xsd index 63c2798f1298..b1d06360cd00 100644 --- a/server/integration/infinispan/src/main/resources/schema/jboss-infinispan-core_9_2.xsd +++ b/server/integration/infinispan/src/main/resources/schema/jboss-infinispan-core_9_2.xsd @@ -373,6 +373,46 @@ + + + + + A non-persistent configuration storage. + + + + + + + A persistent configuration storage which saves runtime configurations to the persistent-location. + + + + + + + A configuration storage which uses the server configuration model for configuration persistence. + Currently only implemented for standalone deployments. + + + + + + + Uses a custom configuration storage implementation. + + + + + + + Class of the custom configuration storage implementation. + + + + + +