/*
* JBoss, Home of Professional Open Source
* Copyright 2011 Red Hat Inc. and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt 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.infinispan.cdi;
import org.infinispan.cdi.event.cachemanager.CacheManagerEventBridge;
import org.infinispan.cdi.interceptor.CachePutInterceptor;
import org.infinispan.cdi.interceptor.CacheRemoveAllInterceptor;
import org.infinispan.cdi.interceptor.CacheRemoveEntryInterceptor;
import org.infinispan.cdi.interceptor.CacheResultInterceptor;
import org.infinispan.cdi.interceptor.literal.CachePutLiteral;
import org.infinispan.cdi.interceptor.literal.CacheRemoveAllLiteral;
import org.infinispan.cdi.interceptor.literal.CacheRemoveEntryLiteral;
import org.infinispan.cdi.interceptor.literal.CacheResultLiteral;
import org.infinispan.cdi.util.Version;
import org.infinispan.cdi.util.logging.Log;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.util.logging.LogFactory;
import org.jboss.solder.bean.BeanBuilder;
import org.jboss.solder.bean.ContextualLifecycle;
import org.jboss.solder.reflection.annotated.AnnotatedTypeBuilder;
import javax.cache.annotation.CachePut;
import javax.cache.annotation.CacheRemoveAll;
import javax.cache.annotation.CacheRemoveEntry;
import javax.cache.annotation.CacheResult;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Default;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AfterDeploymentValidation;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessInjectionTarget;
import javax.enterprise.inject.spi.ProcessProducer;
import javax.enterprise.inject.spi.Producer;
import javax.enterprise.util.AnnotationLiteral;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.jboss.solder.bean.Beans.getQualifiers;
import static org.jboss.solder.reflection.AnnotationInspector.getMetaAnnotation;
import static org.jboss.solder.reflection.Reflections.getRawType;
/**
* The Infinispan CDI extension class.
*
* @author Pete Muir
* @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI
*/
public class InfinispanExtension implements Extension {
private static final Log log = LogFactory.getLog(InfinispanExtension.class, Log.class);
private Producer<RemoteCache<?, ?>> remoteCacheProducer;
private final Set<ConfigurationHolder> configurations;
private final Map<Type, Set<Annotation>> remoteCacheInjectionPoints;
InfinispanExtension() {
this.configurations = new HashSet<InfinispanExtension.ConfigurationHolder>();
this.remoteCacheInjectionPoints = new HashMap<Type, Set<Annotation>>();
}
void registerInterceptorBindings(@Observes BeforeBeanDiscovery event) {
log.version(Version.getVersion());
event.addInterceptorBinding(CacheResult.class);
event.addInterceptorBinding(CachePut.class);
event.addInterceptorBinding(CacheRemoveEntry.class);
event.addInterceptorBinding(CacheRemoveAll.class);
}
void registerCacheResultInterceptor(@Observes ProcessAnnotatedType<CacheResultInterceptor> event) {
event.setAnnotatedType(new AnnotatedTypeBuilder<CacheResultInterceptor>()
.readFromType(event.getAnnotatedType())
.addToClass(CacheResultLiteral.INSTANCE)
.create());
}
void registerCachePutInterceptor(@Observes ProcessAnnotatedType<CachePutInterceptor> event) {
event.setAnnotatedType(new AnnotatedTypeBuilder<CachePutInterceptor>()
.readFromType(event.getAnnotatedType())
.addToClass(CachePutLiteral.INSTANCE)
.create());
}
void registerCacheRemoveEntryInterceptor(@Observes ProcessAnnotatedType<CacheRemoveEntryInterceptor> event) {
event.setAnnotatedType(new AnnotatedTypeBuilder<CacheRemoveEntryInterceptor>()
.readFromType(event.getAnnotatedType())
.addToClass(CacheRemoveEntryLiteral.INSTANCE)
.create());
}
void registerCacheRemoveAllInterceptor(@Observes ProcessAnnotatedType<CacheRemoveAllInterceptor> event) {
event.setAnnotatedType(new AnnotatedTypeBuilder<CacheRemoveAllInterceptor>()
.readFromType(event.getAnnotatedType())
.addToClass(CacheRemoveAllLiteral.INSTANCE)
.create());
}
void saveRemoteCacheProducer(@Observes ProcessProducer<RemoteCacheProducer, RemoteCache<?, ?>> event) {
remoteCacheProducer = event.getProducer();
}
<T> void saveRemoteInjectionPoints(@Observes ProcessInjectionTarget<T> event, BeanManager beanManager) {
final InjectionTarget<T> injectionTarget = event.getInjectionTarget();
for (InjectionPoint injectionPoint : injectionTarget.getInjectionPoints()) {
final Annotated annotated = injectionPoint.getAnnotated();
final Type type = annotated.getBaseType();
final Class<?> rawType = getRawType(annotated.getBaseType());
final Set<Annotation> qualifiers = getQualifiers(beanManager, annotated.getAnnotations());
if (rawType.equals(RemoteCache.class) && qualifiers.isEmpty()) {
qualifiers.add(new AnnotationLiteral<Default>() {});
addRemoteCacheInjectionPoint(type, qualifiers);
} else if (!annotated.isAnnotationPresent(Remote.class)
&& getMetaAnnotation(annotated, Remote.class) != null
&& rawType.isAssignableFrom(RemoteCache.class)) {
addRemoteCacheInjectionPoint(type, qualifiers);
}
}
}
private void addRemoteCacheInjectionPoint(Type type, Set<Annotation> qualifiers) {
final Set<Annotation> currentQualifiers = remoteCacheInjectionPoints.get(type);
if (currentQualifiers == null) {
remoteCacheInjectionPoints.put(type, qualifiers);
} else {
currentQualifiers.addAll(qualifiers);
}
}
void saveCacheConfigurations(@Observes ProcessProducer<?, Configuration> event, BeanManager beanManager) {
final ConfigureCache annotation = event.getAnnotatedMember().getAnnotation(ConfigureCache.class);
if (annotation != null) {
configurations.add(new ConfigurationHolder(
event.getProducer(),
annotation.value(),
getQualifiers(beanManager, event.getAnnotatedMember().getAnnotations())
));
}
}
@SuppressWarnings("unchecked")
void registerRemoteCacheBeans(@Observes AfterBeanDiscovery event, BeanManager beanManager) {
for (Map.Entry<Type, Set<Annotation>> entry : remoteCacheInjectionPoints.entrySet()) {
event.addBean(new BeanBuilder(beanManager)
.readFromType(beanManager.createAnnotatedType(getRawType(entry.getKey())))
.addType(entry.getKey())
.addQualifiers(entry.getValue())
.beanLifecycle(new ContextualLifecycle<RemoteCache<?, ?>>() {
@Override
public RemoteCache<?, ?> create(Bean<RemoteCache<?, ?>> bean, CreationalContext<RemoteCache<?, ?>> ctx) {
return remoteCacheProducer.produce(ctx);
}
@Override
public void destroy(Bean<RemoteCache<?, ?>> bean, RemoteCache<?, ?> instance, CreationalContext<RemoteCache<?, ?>> ctx) {
remoteCacheProducer.dispose(instance);
}
}).create());
}
}
void registerCacheConfigurations(@Observes AfterDeploymentValidation event, CacheManagerEventBridge eventBridge, @Any Instance<EmbeddedCacheManager> cacheManagers, BeanManager beanManager) {
final CreationalContext<Configuration> ctx = beanManager.createCreationalContext(null);
final EmbeddedCacheManager defaultCacheManager = cacheManagers.select(new AnnotationLiteral<Default>() {}).get();
for (ConfigurationHolder oneConfigurationHolder : configurations) {
final String cacheName = oneConfigurationHolder.getName();
final Configuration cacheConfiguration = oneConfigurationHolder.getProducer().produce(ctx);
final Set<Annotation> cacheQualifiers = oneConfigurationHolder.getQualifiers();
// if a specific cache manager is defined for this cache we use it
final Instance<EmbeddedCacheManager> specificCacheManager = cacheManagers.select(cacheQualifiers.toArray(new Annotation[cacheQualifiers.size()]));
final EmbeddedCacheManager cacheManager = specificCacheManager.isUnsatisfied() ? defaultCacheManager : specificCacheManager.get();
// the default configuration is registered by the default cache manager producer
if (!cacheName.trim().isEmpty()) {
if (cacheConfiguration != null) {
cacheManager.defineConfiguration(cacheName, cacheConfiguration);
log.cacheConfigurationDefined(cacheName, cacheManager);
} else if (!cacheManager.getCacheNames().contains(cacheName)) {
cacheManager.defineConfiguration(cacheName, cacheManager.getDefaultCacheConfiguration());
log.cacheConfigurationDefined(cacheName, cacheManager);
}
}
// register cache manager observers
eventBridge.registerObservers(cacheQualifiers, cacheName, cacheManager);
}
}
static class ConfigurationHolder {
private final Producer<Configuration> producer;
private final Set<Annotation> qualifiers;
private final String name;
ConfigurationHolder(Producer<Configuration> producer, String name, Set<Annotation> qualifiers) {
this.producer = producer;
this.name = name;
this.qualifiers = qualifiers;
}
public Producer<Configuration> getProducer() {
return producer;
}
public String getName() {
return name;
}
public Set<Annotation> getQualifiers() {
return qualifiers;
}
}
}