/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.wicket.cdi;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.InjectionTarget;
import org.apache.wicket.util.collections.ClassMetaCache;
/**
* Manages lifecycle of non-contextual (non-CDI-managed) objects
*
* @author igor
*
* @param <T>
*/
public class NonContextual<T>
{
private static final Object lock = new Object();
private static volatile Map<BeanManager, ClassMetaCache<NonContextual<?>>> cache = Collections.emptyMap();
final InjectionTarget<T> it;
final BeanManager manager;
/**
* Undeploys specified bean manager from cache
*
* @param beanManager
*/
public static void undeploy(BeanManager beanManager)
{
if (cache.containsKey(beanManager))
{
synchronized (lock)
{
// copy-on-write the cache
Map<BeanManager, ClassMetaCache<NonContextual<?>>> newCache = new WeakHashMap<>(
cache);
newCache.remove(beanManager);
cache = Collections.unmodifiableMap(newCache);
}
}
}
/**
* Convenience factory method for an instance, see {@link #of(Class, BeanManager).
*
* @param <T>
* @param beanManager
* @return The NonContextual for the instance's class managed by the given manager
*/
@SuppressWarnings("unchecked")
public static <T> NonContextual<T> of(T t, BeanManager beanManager)
{
// cast is necessary for Eclipse compiler :/
return (NonContextual<T>)of(t.getClass(), beanManager);
}
/**
* Factory method for creating noncontextual instances
*
* @param <T>
* @param clazz
* @param manager
* @return The NonContextual for the given class managed by the given manager
*/
public static <T> NonContextual<T> of(Class<? extends T> clazz, BeanManager manager)
{
ClassMetaCache<NonContextual<?>> meta = getCache(manager);
@SuppressWarnings("unchecked")
NonContextual<T> nc = (NonContextual<T>)meta.get(clazz);
if (nc == null)
{
nc = new NonContextual<>(manager, clazz);
meta.put(clazz, nc);
}
return nc;
}
private static ClassMetaCache<NonContextual<?>> getCache(BeanManager manager)
{
ClassMetaCache<NonContextual<?>> meta = cache.get(manager);
if (meta == null)
{
synchronized (lock)
{
meta = cache.get(manager);
if (meta == null)
{
meta = new ClassMetaCache<>();
// copy-on-write the cache
Map<BeanManager, ClassMetaCache<NonContextual<?>>> newCache = new WeakHashMap<>(
cache);
newCache.put(manager, meta);
cache = Collections.unmodifiableMap(newCache);
}
}
}
return meta;
}
@SuppressWarnings("unchecked")
private NonContextual(BeanManager manager, Class<? extends T> clazz)
{
this.manager = manager;
AnnotatedType<? extends T> type = manager.createAnnotatedType(clazz);
this.it = (InjectionTarget<T>)manager.createInjectionTarget(type);
}
/**
* Injects the instance and calls any {@link PostConstruct} methods
*
* @param instance
*/
public void postConstruct(T instance)
{
CreationalContext<T> cc = manager.createCreationalContext(null);
it.inject(instance, cc);
it.postConstruct(instance);
}
/**
* Injects the instance
*
* @param instance
*/
public void inject(T instance)
{
CreationalContext<T> cc = manager.createCreationalContext(null);
it.inject(instance, cc);
}
/**
* Calls any {@link PreDestroy} methods and destroys any injected dependencies that need to be
* destroyed.
*
* @param instance
*/
public void preDestroy(T instance)
{
it.preDestroy(instance);
}
}