/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.ejb.server;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.ejb.TimedObject;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.InjectionTarget;
import com.caucho.config.inject.BeanBuilder;
import com.caucho.config.inject.CreationalContextImpl;
import com.caucho.config.inject.DependentCreationalContext;
import com.caucho.config.inject.InjectManager;
import com.caucho.config.inject.InjectionTargetBuilder;
import com.caucho.config.inject.ManagedBeanImpl;
import com.caucho.config.inject.OwnerCreationalContext;
import com.caucho.config.j2ee.PostConstructProgram;
import com.caucho.config.program.ConfigProgram;
import com.caucho.ejb.cfg.PostConstructConfig;
import com.caucho.ejb.cfg.PreDestroyConfig;
import com.caucho.ejb.timer.EjbTimerService;
import com.caucho.ejb.xa.XaInterceptor;
/**
* Creates an configures an ejb instance
*/
public class EjbInjectionTarget<T> {
private AbstractEjbBeanManager<T> _manager;
private Class<T> _ejbClass;
private AnnotatedType<T> _annotatedType;
private ManagedBeanImpl<T> _bean;
private AtomicBoolean _isBind = new AtomicBoolean();
private ClassLoader _envLoader;
private InjectionTarget<T> _injectionTarget;
private ArrayList<ConfigProgram> _resourceProgram;
private PreDestroyConfig _preDestroyConfig;
private PostConstructConfig _postConstructConfig;
private Method _cauchoPostConstruct;
private Method _timeoutMethod;
private TimerService _timerService;
EjbInjectionTarget(AbstractEjbBeanManager<T> manager,
AnnotatedType<T> annotatedType)
{
_manager = manager;
_ejbClass = annotatedType.getJavaClass();
_annotatedType = annotatedType;
try {
_cauchoPostConstruct = _ejbClass.getDeclaredMethod("__caucho_postConstruct");
_cauchoPostConstruct.setAccessible(true);
} catch (NoSuchMethodException e) {
}
}
/**
* Sets the classloader for the EJB's private environment
*
* @param loader the environment classloader
*/
public void setEnvLoader(ClassLoader envLoader)
{
_envLoader = envLoader;
}
/**
* Sets the injection target
*/
public void setInjectionTarget(InjectionTarget<T> injectionTarget)
{
_injectionTarget = injectionTarget;
if (injectionTarget instanceof InjectionTargetBuilder<?>) {
InjectionTargetBuilder<T> targetImpl
= (InjectionTargetBuilder<T>) injectionTarget;
targetImpl.setGenerateInterception(false);
// Special processing required for the @Singleton XA support
ConfigProgram []initProgram = targetImpl.getPostConstructProgram();
ConfigProgram []extInitProgram = new ConfigProgram[initProgram.length];
for (int i = 0; i < initProgram.length; i++) {
ConfigProgram program = null;
if (program instanceof PostConstructProgram) {
PostConstructProgram pcProgram = (PostConstructProgram) program;
AnnotatedMethod<?> annMethod = pcProgram.getAnnotatedMethod();
if (annMethod != null) {
program = XaInterceptor.create(annMethod);
}
}
if (program != null)
extInitProgram[i] = program;
else
extInitProgram[i] = initProgram[i];
}
targetImpl.setPostConstructProgram(extInitProgram);
}
}
/**
* Gets the injection target
*/
public InjectionTarget<T> getInjectionTarget()
{
return _injectionTarget;
}
public PostConstructConfig getPostConstruct()
{
return _postConstructConfig;
}
public PreDestroyConfig getPreDestroy()
{
return _preDestroyConfig;
}
public void setPostConstruct(PostConstructConfig postConstruct)
{
_postConstructConfig = postConstruct;
}
public void setPreDestroy(PreDestroyConfig preDestroy)
{
_preDestroyConfig = preDestroy;
}
public TimerService getTimerService()
{
return _timerService;
}
public Method getTimeoutMethod()
{
return _timeoutMethod;
}
public void registerInjection()
{
if (_bean != null)
return;
Thread thread = Thread.currentThread();
ClassLoader loader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(_manager.getClassLoader());
InjectManager cdiManager = InjectManager.create();
ManagedBeanImpl<T> managedBean
= cdiManager.createManagedBean(_annotatedType);
_bean = managedBean;
_timeoutMethod = getTimeoutMethod(_bean.getBeanClass());
if (_timeoutMethod != null)
_timerService = new EjbTimerService(_manager);
// Injection binding occurs in the start phase
// _resourceProgram = _manager.getResourceProgram(_ejbClass);
if (_timerService != null) {
BeanBuilder<TimerService> factory = cdiManager.createBeanFactory(TimerService.class);
cdiManager.addBean(factory.singleton(_timerService));
}
} finally {
thread.setContextClassLoader(loader);
}
}
public void bindInjection()
{
registerInjection();
if (! _isBind.compareAndSet(false, true))
return;
Thread thread = Thread.currentThread();
ClassLoader loader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(_manager.getClassLoader());
InjectManager cdiManager= InjectManager.create();
setInjectionTarget(_bean.getInjectionTarget());
// server/4751
if (_injectionTarget == null) {
_injectionTarget = cdiManager.createInjectionTarget(_ejbClass);
_injectionTarget.getInjectionPoints();
}
} finally {
thread.setContextClassLoader(loader);
}
}
Bean<?> getBean()
{
return _bean;
}
private Method getTimeoutMethod(Class<?> targetBean)
{
if (TimedObject.class.isAssignableFrom(targetBean)) {
try {
return targetBean.getMethod("ejbTimeout", Timer.class);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
for (Method method : targetBean.getMethods()) {
if (method.getAnnotation(Timeout.class) != null) {
return method;
}
}
return null;
}
public T newInstance()
{
return newInstance(null);
}
public T newInstance(CreationalContextImpl<?> parentEnv)
{
if (! _isBind.get())
bindInjection();
T instance = CreationalContextImpl.find(parentEnv, _bean);
if (instance != null)
return instance;
if (parentEnv == null)
parentEnv = new OwnerCreationalContext<T>(_bean);
// XXX: circular for stateful
CreationalContextImpl<T> env
= new DependentCreationalContext<T>(_bean, parentEnv, null);
instance = _injectionTarget.produce(env);
_injectionTarget.inject(instance, env);
postConstruct(instance);
return instance;
}
protected void postConstruct(T instance)
{
_injectionTarget.postConstruct(instance);
}
/**
* Initialize an instance
*/
public <X> void initInstance(T instance,
InjectionTarget<T> target,
X proxy,
CreationalContextImpl<X> proxyEnv)
{
Bean<T> bean = _bean;
if (proxyEnv != null && bean != null) {
// server/4762
// env.put((AbstractBean) bean, proxy);
proxyEnv.push(proxy);
}
Thread thread = Thread.currentThread();
ClassLoader oldLoader = thread.getContextClassLoader();
CreationalContextImpl<T> cxt
= new DependentCreationalContext<T>(bean, proxyEnv, null);
try {
thread.setContextClassLoader(_envLoader);
if (target != null) {
target.inject(instance, cxt);
}
InjectionTarget<T> selfInjectionTarget = getInjectionTarget();
if (selfInjectionTarget != null) {
if (target != selfInjectionTarget) {
selfInjectionTarget.inject(instance, cxt);
}
}
for (ConfigProgram program : _resourceProgram) {
program.inject(instance, cxt);
}
if (selfInjectionTarget != null) {
selfInjectionTarget.postConstruct(instance);
}
} finally {
thread.setContextClassLoader(oldLoader);
}
/*
if (cxt != null && bean != null)
cxt.remove(bean);
*/
}
/**
* Remove an object.
*/
public void destroyInstance(T instance)
{
Thread thread = Thread.currentThread();
ClassLoader oldLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(_envLoader);
if (getInjectionTarget() != null) {
getInjectionTarget().preDestroy(instance);
}
} finally {
thread.setContextClassLoader(oldLoader);
}
}
public String toString()
{
return getClass().getSimpleName() + "[" + _ejbClass + "]";
}
}