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}