001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.cdi;
018
019import java.util.Collections;
020import java.util.Map;
021import java.util.WeakHashMap;
022
023import javax.annotation.PostConstruct;
024import javax.annotation.PreDestroy;
025import javax.enterprise.context.spi.CreationalContext;
026import javax.enterprise.inject.spi.AnnotatedType;
027import javax.enterprise.inject.spi.BeanManager;
028import javax.enterprise.inject.spi.InjectionTarget;
029
030import org.apache.wicket.util.collections.ClassMetaCache;
031
032/**
033 * Manages lifecycle of non-contextual (non-CDI-managed) objects
034 * 
035 * @param <T>
036 * @author igor
037 */
038public class NonContextual<T>
039{
040        private static final Object lock = new Object();
041        private static volatile Map<BeanManager, ClassMetaCache<NonContextual<?>>> cache = Collections
042                        .emptyMap();
043
044        final InjectionTarget<T> it;
045
046        /**
047         * Undeploys the looked up bean manager from cache
048         */
049        public static void undeploy()
050        {
051                if (cache.containsKey(BeanManagerLookup.lookup()))
052                {
053                        synchronized (lock)
054                        {
055                                // copy-on-write the cache
056                                Map<BeanManager, ClassMetaCache<NonContextual<?>>> newCache = new WeakHashMap<BeanManager, ClassMetaCache<NonContextual<?>>>(
057                                                cache);
058                                newCache.remove(BeanManagerLookup.lookup());
059                                cache = Collections.unmodifiableMap(newCache);
060                        }
061                }
062        }
063
064        /**
065         * Convenience factory method for an instance, see {@link #of(Class)}.
066         * 
067         * @param <T>
068         * @param t
069         * @return The NonContextual for the instance's class
070         */
071        @SuppressWarnings("unchecked")
072        public static <T> NonContextual<T> of(T t) {
073                // cast is necessary for Eclipse compiler :/
074                return (NonContextual<T>)of(t.getClass());
075        }
076
077        /**
078         * Factory method for creating non-contextual instances
079         * 
080         * @param <T>
081         * @param clazz
082         * @return The NonContextual for the given class
083         */
084        public static <T> NonContextual<T> of(Class<? extends T> clazz)
085        {
086                ClassMetaCache<NonContextual<?>> meta = getCache();
087
088                @SuppressWarnings("unchecked")
089                NonContextual<T> nc = (NonContextual<T>)meta.get(clazz);
090
091                if (nc == null)
092                {
093                        nc = new NonContextual<T>(clazz);
094                        meta.put(clazz, nc);
095                }
096                return nc;
097        }
098
099        private static ClassMetaCache<NonContextual<?>> getCache()
100        {
101                ClassMetaCache<NonContextual<?>> meta = cache.get(BeanManagerLookup.lookup());
102                if (meta == null)
103                {
104                        synchronized (lock)
105                        {
106                                BeanManager manager = BeanManagerLookup.lookup();
107                                meta = cache.get(manager);
108                                if (meta == null)
109                                {
110                                        meta = new ClassMetaCache<NonContextual<?>>();
111
112                                        // copy-on-write the cache
113                                        Map<BeanManager, ClassMetaCache<NonContextual<?>>> newCache = new WeakHashMap<BeanManager, ClassMetaCache<NonContextual<?>>>(
114                                                        cache);
115                                        newCache.put(manager, meta);
116                                        cache = Collections.unmodifiableMap(newCache);
117                                }
118                        }
119                }
120                return meta;
121        }
122
123        @SuppressWarnings("unchecked")
124        private NonContextual(Class<? extends T> clazz)
125        {
126                BeanManager manager = BeanManagerLookup.lookup();
127                AnnotatedType<? extends T> type = manager.createAnnotatedType(clazz);
128                this.it = (InjectionTarget<T>) manager.getInjectionTargetFactory(type)
129                        .createInjectionTarget(null);
130        }
131
132        /**
133         * Injects the instance and calls any {@link PostConstruct} methods
134         * 
135         * @param instance
136         */
137        public void postConstruct(T instance)
138        {
139                CreationalContext<T> cc = BeanManagerLookup.lookup().createCreationalContext(null);
140                it.inject(instance, cc);
141                it.postConstruct(instance);
142        }
143
144        /**
145         * Injects the instance
146         * 
147         * @param instance
148         */
149        public void inject(T instance)
150        {
151                CreationalContext<T> cc = BeanManagerLookup.lookup().createCreationalContext(null);
152                it.inject(instance, cc);
153        }
154
155        /**
156         * Calls any {@link PreDestroy} methods and destroys any injected
157         * dependencies that need to be destroyed.
158         * 
159         * @param instance
160         */
161        public void preDestroy(T instance)
162        {
163                it.preDestroy(instance);
164        }
165}