/* * 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 static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import java.util.ArrayList; import java.util.Collection; import org.infinispan.Cache; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.persistence.factory.CacheStoreFactory; import org.infinispan.server.infinispan.spi.service.CacheContainerServiceName; import org.infinispan.server.infinispan.spi.service.CacheServiceName; import org.infinispan.server.infinispan.task.ServerTaskRegistry; import org.infinispan.server.infinispan.task.ServerTaskRegistryService; import org.jboss.as.clustering.infinispan.cs.factory.DeployedCacheStoreFactory; import org.jboss.as.clustering.infinispan.cs.factory.DeployedCacheStoreFactoryService; 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.PathAddress; import org.jboss.as.controller.registry.Resource; import org.jboss.as.naming.ManagedReferenceInjector; import org.jboss.as.naming.ServiceBasedNamingStore; import org.jboss.as.naming.deployment.ContextNames; import org.jboss.as.naming.service.BinderService; import org.jboss.dmr.ModelNode; import org.jboss.logging.Logger; import org.jboss.msc.inject.Injector; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceTarget; import org.jboss.msc.value.InjectedValue; import org.jboss.msc.value.Value; import org.jboss.tm.XAResourceRecoveryRegistry; /** * Base class for cache add handlers * * @author Richard Achmatowicz (c) 2011 Red Hat Inc. */ public abstract class CacheAdd extends AbstractAddStepHandler implements RestartableServiceHandler { private static final Logger log = Logger.getLogger(CacheAdd.class.getPackage().getName()); final CacheMode mode; CacheAdd(CacheMode mode) { this.mode = mode; } @Override protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException { this.populate(operation, model); } @Override protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { // Because we use child resources in a read-only manner to configure the cache, replace the local model with the full model ModelNode cacheModel = Resource.Tools.readModel(context.readResource(PathAddress.EMPTY_ADDRESS)); // we also need the containerModel PathAddress containerAddress = getCacheContainerAddressFromOperation(operation); ModelNode containerModel = context.readResourceFromRoot(containerAddress).getModel(); // install the services from a reusable method this.installRuntimeServices(context, operation, containerModel, cacheModel); } @Override public Collection<ServiceController<?>> installRuntimeServices(OperationContext context, ModelNode operation, ModelNode containerModel, ModelNode cacheModel) throws OperationFailedException { // get all required addresses, names and service names PathAddress cacheAddress = getCacheAddressFromOperation(operation); PathAddress containerAddress = getCacheContainerAddressFromOperation(operation); String cacheName = cacheAddress.getLastElement().getValue(); String containerName = containerAddress.getLastElement().getValue(); // get model attributes final String configuration = CacheResource.CONFIGURATION.resolveModelAttribute(context, cacheModel).asString(); StartMode startMode = StartMode.valueOf(CacheConfigurationResource.START.resolveModelAttribute(context, cacheModel).asString()); if (startMode != StartMode.EAGER) { log.warnf("Ignoring start mode [%s] of cache service [%s], as EAGER is the only supported mode", startMode, cacheName); startMode = StartMode.EAGER; } final ServiceController.Mode initialMode = startMode.getMode(); ServiceTarget target = context.getServiceTarget(); Collection<ServiceController<?>> controllers = new ArrayList<>(2); // now install the corresponding cache service (starts a configured cache) controllers.add(this.installCacheService(target, containerName, cacheName, initialMode, configuration)); // install a name service entry for the cache ModelNode resolvedValue = CacheConfigurationResource.JNDI_NAME.resolveModelAttribute(context, cacheModel); final String jndiName = InfinispanJndiName.createCacheJndiName(resolvedValue.isDefined() ? resolvedValue.asString() : null, containerName, cacheName); controllers.add(this.installJndiService(target, containerName, cacheName, jndiName)); log.debugf("Cache service for cache %s installed for container %s", cacheName, containerName); return controllers; } @Override public void removeRuntimeServices(OperationContext context, ModelNode operation, ModelNode containerModel, ModelNode cacheModel) throws OperationFailedException { // get container and cache addresses final PathAddress cacheAddress = getCacheAddressFromOperation(operation) ; final PathAddress containerAddress = getCacheContainerAddressFromOperation(operation) ; // get container and cache names final String cacheName = cacheAddress.getLastElement().getValue() ; final String containerName = containerAddress.getLastElement().getValue() ; // remove the binder service ModelNode resolvedValue = CacheConfigurationResource.JNDI_NAME.resolveModelAttribute(context, cacheModel); final String jndiName = InfinispanJndiName.createCacheJndiName(resolvedValue.isDefined() ? resolvedValue.asString() : null, containerName, cacheName); context.removeService(ContextNames.bindInfoFor(jndiName).getBinderServiceName()); // remove the CacheService instance context.removeService(CacheServiceName.CACHE.getServiceName(containerName, cacheName)); log.debugf("cache %s removed for container %s", cacheName, containerName); } protected PathAddress getCacheAddressFromOperation(ModelNode operation) { return PathAddress.pathAddress(operation.get(OP_ADDR)) ; } protected PathAddress getCacheContainerAddressFromOperation(ModelNode operation) { final PathAddress cacheAddress = getCacheAddressFromOperation(operation) ; final PathAddress containerAddress = cacheAddress.subAddress(0, cacheAddress.size()-1) ; return containerAddress ; } ServiceController<?> installCacheService(ServiceTarget target, String containerName, String cacheName, ServiceController.Mode initialMode, String configurationName) { final InjectedValue<EmbeddedCacheManager> container = new InjectedValue<>(); final CacheDependencies cacheDependencies = new CacheDependencies(container); final Service<Cache<Object, Object>> service = new CacheService<>(cacheName, configurationName, cacheDependencies); final ServiceBuilder<?> builder = target.addService(CacheServiceName.CACHE.getServiceName(containerName, cacheName), service) .addDependency(CacheServiceName.CONFIGURATION.getServiceName(containerName, configurationName)) .addDependency(CacheContainerServiceName.CACHE_CONTAINER.getServiceName(containerName), EmbeddedCacheManager.class, container) .setInitialMode(initialMode) ; builder.addDependency(DeployedCacheStoreFactoryService.SERVICE_NAME, DeployedCacheStoreFactory.class, cacheDependencies.getDeployedCacheStoreFactoryInjector()); builder.addDependency(ServerTaskRegistryService.SERVICE_NAME, ServerTaskRegistry.class, cacheDependencies.getDeployedTaskRegistryInjector()); return builder.install(); } @SuppressWarnings("rawtypes") ServiceController<?> installJndiService(ServiceTarget target, String containerName, String cacheName, String jndiName) { final ServiceName cacheServiceName = CacheServiceName.CACHE.getServiceName(containerName, cacheName); final ContextNames.BindInfo bindInfo = ContextNames.bindInfoFor(jndiName); final BinderService binder = new BinderService(bindInfo.getBindName()); return target.addService(bindInfo.getBinderServiceName(), binder) .addAliases(ContextNames.JAVA_CONTEXT_SERVICE_NAME.append(jndiName)) .addDependency(cacheServiceName, Cache.class, new ManagedReferenceInjector<Cache>(binder.getManagedObjectInjector())) .addDependency(bindInfo.getParentContextServiceName(), ServiceBasedNamingStore.class, binder.getNamingStoreInjector()) .setInitialMode(ServiceController.Mode.PASSIVE) .install() ; } private <T> void addDependency(ServiceBuilder<?> builder, Dependency<T> dependency) { final ServiceName name = dependency.getName(); final Injector<T> injector = dependency.getInjector(); if (injector != null) { builder.addDependency(name, dependency.getType(), injector); } else { builder.addDependency(name); } } /** * Transfer elements common to both operations and models * * @param fromModel * @param toModel */ void populate(ModelNode fromModel, ModelNode toModel) throws OperationFailedException { for(AttributeDefinition attr : CacheResource.CACHE_ATTRIBUTES) { attr.validateAndSet(fromModel, toModel); } } /* * Allows us to store dependency requirements for later processing. */ protected class Dependency<I> { private final ServiceName name; private final Class<I> type; private final Injector<I> target; Dependency(ServiceName name) { this(name, null, null); } Dependency(ServiceName name, Class<I> type, Injector<I> target) { this.name = name; this.type = type; this.target = target; } ServiceName getName() { return name; } public Class<I> getType() { return type; } public Injector<I> getInjector() { return target; } } private static class CacheDependencies implements CacheService.Dependencies { private final Value<EmbeddedCacheManager> container; private final InjectedValue<XAResourceRecoveryRegistry> recoveryRegistry = new InjectedValue<>(); private final InjectedValue<DeployedCacheStoreFactory> deployedCacheStoreFactory = new InjectedValue<>(); private final InjectedValue<ServerTaskRegistry> deployedTaskRegistry = new InjectedValue<>(); CacheDependencies(Value<EmbeddedCacheManager> container) { this.container = container; } Injector<XAResourceRecoveryRegistry> getRecoveryRegistryInjector() { return this.recoveryRegistry; } public InjectedValue<DeployedCacheStoreFactory> getDeployedCacheStoreFactoryInjector() { return deployedCacheStoreFactory; } public InjectedValue<ServerTaskRegistry> getDeployedTaskRegistryInjector() { return deployedTaskRegistry; } public ServerTaskRegistry getDeployedTaskRegistry() { return deployedTaskRegistry.getValue(); } @Override public EmbeddedCacheManager getCacheContainer() { return this.container.getValue(); } @Override public XAResourceRecoveryRegistry getRecoveryRegistry() { return this.recoveryRegistry.getOptionalValue(); } @Override public CacheStoreFactory getDeployedCacheStoreFactory() { return deployedCacheStoreFactory.getValue(); } } }