001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.jcs3.jcache.cdi; 020 021import java.lang.annotation.Annotation; 022import java.lang.reflect.Type; 023import java.util.Collections; 024import java.util.HashSet; 025import java.util.Set; 026import java.util.concurrent.atomic.AtomicInteger; 027import javax.cache.annotation.CachePut; 028import javax.cache.annotation.CacheRemove; 029import javax.cache.annotation.CacheRemoveAll; 030import javax.cache.annotation.CacheResult; 031import javax.enterprise.context.ApplicationScoped; 032import javax.enterprise.context.spi.CreationalContext; 033import javax.enterprise.event.Observes; 034import javax.enterprise.inject.Any; 035import javax.enterprise.inject.Default; 036import javax.enterprise.inject.spi.AfterBeanDiscovery; 037import javax.enterprise.inject.spi.AnnotatedType; 038import javax.enterprise.inject.spi.Bean; 039import javax.enterprise.inject.spi.BeanManager; 040import javax.enterprise.inject.spi.BeforeBeanDiscovery; 041import javax.enterprise.inject.spi.Extension; 042import javax.enterprise.inject.spi.InjectionPoint; 043import javax.enterprise.inject.spi.InjectionTarget; 044import javax.enterprise.inject.spi.PassivationCapable; 045import javax.enterprise.inject.spi.ProcessAnnotatedType; 046import javax.enterprise.util.AnnotationLiteral; 047 048import static java.util.Arrays.asList; 049 050// TODO: observe annotated type (or maybe sthg else) to cache data and inject this extension (used as metadata cache) 051// to get class model and this way allow to add cache annotation on the fly - == avoid java pure reflection to get metadata 052public class MakeJCacheCDIInterceptorFriendly implements Extension 053{ 054 private static final AtomicInteger id = new AtomicInteger(); 055 private static final boolean USE_ID = !Boolean.getBoolean("org.apache.commons.jcs3.cdi.skip-id"); 056 057 private boolean needHelper = true; 058 059 protected void discoverInterceptorBindings(final @Observes BeforeBeanDiscovery beforeBeanDiscoveryEvent, 060 final BeanManager bm) 061 { 062 // CDI 1.1 will just pick createAnnotatedType(X) as beans so we'll skip our HelperBean 063 // but CDI 1.0 needs our HelperBean + interceptors in beans.xml like: 064 /* 065 <beans xmlns="http://java.sun.com/xml/ns/javaee" 066 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 067 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 068 http://java.sun.com/xml/ns/javaee/beans_1_0.xsd"> 069 <interceptors> 070 <class>org.apache.commons.jcs3.jcache.cdi.CacheResultInterceptor</class> 071 <class>org.apache.commons.jcs3.jcache.cdi.CacheRemoveAllInterceptor</class> 072 <class>org.apache.commons.jcs3.jcache.cdi.CacheRemoveInterceptor</class> 073 <class>org.apache.commons.jcs3.jcache.cdi.CachePutInterceptor</class> 074 </interceptors> 075 </beans> 076 */ 077 bm.createAnnotatedType(CDIJCacheHelper.class); 078 for (final Class<?> interceptor : asList( 079 CachePutInterceptor.class, CacheRemoveInterceptor.class, 080 CacheRemoveAllInterceptor.class, CacheResultInterceptor.class)) { 081 beforeBeanDiscoveryEvent.addAnnotatedType(bm.createAnnotatedType(interceptor)); 082 } 083 for (final Class<? extends Annotation> interceptor : asList( 084 CachePut.class, CacheRemove.class, 085 CacheRemoveAll.class, CacheResult.class)) { 086 beforeBeanDiscoveryEvent.addInterceptorBinding(interceptor); 087 } 088 } 089 090 protected void addHelper(final @Observes AfterBeanDiscovery afterBeanDiscovery, 091 final BeanManager bm) 092 { 093 if (!needHelper) { 094 return; 095 } 096 /* CDI >= 1.1 only. Actually we shouldn't go here with CDI 1.1 since we defined the annotated type for the helper 097 final AnnotatedType<CDIJCacheHelper> annotatedType = bm.createAnnotatedType(CDIJCacheHelper.class); 098 final BeanAttributes<CDIJCacheHelper> beanAttributes = bm.createBeanAttributes(annotatedType); 099 final InjectionTarget<CDIJCacheHelper> injectionTarget = bm.createInjectionTarget(annotatedType); 100 final Bean<CDIJCacheHelper> bean = bm.createBean(beanAttributes, CDIJCacheHelper.class, new InjectionTargetFactory<CDIJCacheHelper>() { 101 @Override 102 public InjectionTarget<CDIJCacheHelper> createInjectionTarget(Bean<CDIJCacheHelper> bean) { 103 return injectionTarget; 104 } 105 }); 106 */ 107 final AnnotatedType<CDIJCacheHelper> annotatedType = bm.createAnnotatedType(CDIJCacheHelper.class); 108 final InjectionTarget<CDIJCacheHelper> injectionTarget = bm.createInjectionTarget(annotatedType); 109 final HelperBean bean = new HelperBean(annotatedType, injectionTarget, findIdSuffix()); 110 afterBeanDiscovery.addBean(bean); 111 } 112 113 protected void vetoScannedCDIJCacheHelperQualifiers(final @Observes ProcessAnnotatedType<CDIJCacheHelper> pat) { 114 if (!needHelper) { // already seen, shouldn't really happen,just a protection 115 pat.veto(); 116 } 117 needHelper = false; 118 } 119 120 // TODO: make it better for ear+cluster case with CDI 1.0 121 private static String findIdSuffix() { 122 // big disadvantage is all deployments of a cluster needs to be in the exact same order but it works with ears 123 if (USE_ID) { 124 return "lib" + id.incrementAndGet(); 125 } 126 return "default"; 127 } 128 129 public static class HelperBean implements Bean<CDIJCacheHelper>, PassivationCapable { 130 private final AnnotatedType<CDIJCacheHelper> at; 131 private final InjectionTarget<CDIJCacheHelper> it; 132 private final HashSet<Annotation> qualifiers; 133 private final String id; 134 135 public HelperBean(final AnnotatedType<CDIJCacheHelper> annotatedType, 136 final InjectionTarget<CDIJCacheHelper> injectionTarget, 137 final String id) { 138 this.at = annotatedType; 139 this.it = injectionTarget; 140 this.id = "JCS#CDIHelper#" + id; 141 142 this.qualifiers = new HashSet<>(); 143 this.qualifiers.add(new AnnotationLiteral<Default>() { 144 145 /** 146 * 147 */ 148 private static final long serialVersionUID = 3314657767813459983L;}); 149 this.qualifiers.add(new AnnotationLiteral<Any>() { 150 151 /** 152 * 153 */ 154 private static final long serialVersionUID = 7419841275942488170L;}); 155 } 156 157 @Override 158 public Set<InjectionPoint> getInjectionPoints() { 159 return it.getInjectionPoints(); 160 } 161 162 @Override 163 public Class<?> getBeanClass() { 164 return at.getJavaClass(); 165 } 166 167 @Override 168 public boolean isNullable() { 169 return false; 170 } 171 172 @Override 173 public Set<Type> getTypes() { 174 return at.getTypeClosure(); 175 } 176 177 @Override 178 public Set<Annotation> getQualifiers() { 179 return qualifiers; 180 } 181 182 @Override 183 public Class<? extends Annotation> getScope() { 184 return ApplicationScoped.class; 185 } 186 187 @Override 188 public String getName() { 189 return null; 190 } 191 192 @Override 193 public Set<Class<? extends Annotation>> getStereotypes() { 194 return Collections.emptySet(); 195 } 196 197 @Override 198 public boolean isAlternative() { 199 return false; 200 } 201 202 @Override 203 public CDIJCacheHelper create(final CreationalContext<CDIJCacheHelper> context) { 204 final CDIJCacheHelper produce = it.produce(context); 205 it.inject(produce, context); 206 it.postConstruct(produce); 207 return produce; 208 } 209 210 @Override 211 public void destroy(final CDIJCacheHelper instance, final CreationalContext<CDIJCacheHelper> context) { 212 it.preDestroy(instance); 213 it.dispose(instance); 214 context.release(); 215 } 216 217 @Override 218 public String getId() { 219 return id; 220 } 221 } 222}