View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.jcs3.jcache.cdi;
20  
21  import java.lang.annotation.Annotation;
22  import java.lang.reflect.Type;
23  import java.util.Collections;
24  import java.util.HashSet;
25  import java.util.Set;
26  import java.util.concurrent.atomic.AtomicInteger;
27  import javax.cache.annotation.CachePut;
28  import javax.cache.annotation.CacheRemove;
29  import javax.cache.annotation.CacheRemoveAll;
30  import javax.cache.annotation.CacheResult;
31  import javax.enterprise.context.ApplicationScoped;
32  import javax.enterprise.context.spi.CreationalContext;
33  import javax.enterprise.event.Observes;
34  import javax.enterprise.inject.Any;
35  import javax.enterprise.inject.Default;
36  import javax.enterprise.inject.spi.AfterBeanDiscovery;
37  import javax.enterprise.inject.spi.AnnotatedType;
38  import javax.enterprise.inject.spi.Bean;
39  import javax.enterprise.inject.spi.BeanManager;
40  import javax.enterprise.inject.spi.BeforeBeanDiscovery;
41  import javax.enterprise.inject.spi.Extension;
42  import javax.enterprise.inject.spi.InjectionPoint;
43  import javax.enterprise.inject.spi.InjectionTarget;
44  import javax.enterprise.inject.spi.PassivationCapable;
45  import javax.enterprise.inject.spi.ProcessAnnotatedType;
46  import javax.enterprise.util.AnnotationLiteral;
47  
48  import static java.util.Arrays.asList;
49  
50  // TODO: observe annotated type (or maybe sthg else) to cache data and inject this extension (used as metadata cache)
51  // to get class model and this way allow to add cache annotation on the fly - == avoid java pure reflection to get metadata
52  public class MakeJCacheCDIInterceptorFriendly implements Extension
53  {
54      private static final AtomicInteger id = new AtomicInteger();
55      private static final boolean USE_ID = !Boolean.getBoolean("org.apache.commons.jcs3.cdi.skip-id");
56  
57      private boolean needHelper = true;
58  
59      protected void discoverInterceptorBindings(final @Observes BeforeBeanDiscovery beforeBeanDiscoveryEvent,
60                                                 final BeanManager bm)
61      {
62          // CDI 1.1 will just pick createAnnotatedType(X) as beans so we'll skip our HelperBean
63          // but CDI 1.0 needs our HelperBean + interceptors in beans.xml like:
64          /*
65          <beans xmlns="http://java.sun.com/xml/ns/javaee"
66                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
67                 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
68                http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
69            <interceptors>
70              <class>org.apache.commons.jcs3.jcache.cdi.CacheResultInterceptor</class>
71              <class>org.apache.commons.jcs3.jcache.cdi.CacheRemoveAllInterceptor</class>
72              <class>org.apache.commons.jcs3.jcache.cdi.CacheRemoveInterceptor</class>
73              <class>org.apache.commons.jcs3.jcache.cdi.CachePutInterceptor</class>
74            </interceptors>
75          </beans>
76           */
77          bm.createAnnotatedType(CDIJCacheHelper.class);
78          for (final Class<?> interceptor : asList(
79                  CachePutInterceptor.class, CacheRemoveInterceptor.class,
80                  CacheRemoveAllInterceptor.class, CacheResultInterceptor.class)) {
81              beforeBeanDiscoveryEvent.addAnnotatedType(bm.createAnnotatedType(interceptor));
82          }
83          for (final Class<? extends Annotation> interceptor : asList(
84                  CachePut.class, CacheRemove.class,
85                  CacheRemoveAll.class, CacheResult.class)) {
86              beforeBeanDiscoveryEvent.addInterceptorBinding(interceptor);
87          }
88      }
89  
90      protected void addHelper(final @Observes AfterBeanDiscovery afterBeanDiscovery,
91                               final BeanManager bm)
92      {
93          if (!needHelper) {
94              return;
95          }
96          /* CDI >= 1.1 only. Actually we shouldn't go here with CDI 1.1 since we defined the annotated type for the helper
97          final AnnotatedType<CDIJCacheHelper> annotatedType = bm.createAnnotatedType(CDIJCacheHelper.class);
98          final BeanAttributes<CDIJCacheHelper> beanAttributes = bm.createBeanAttributes(annotatedType);
99          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 }