/*
* 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.session;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RolesAllowed;
import javax.ejb.SessionContext;
import javax.ejb.TimerService;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.Specializes;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedParameter;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.enterprise.inject.spi.SessionBeanType;
import com.caucho.config.ConfigException;
import com.caucho.config.gen.BeanGenerator;
import com.caucho.config.inject.BeanBuilder;
import com.caucho.config.inject.InjectManager;
import com.caucho.config.inject.ManagedBeanImpl;
import com.caucho.config.inject.OwnerCreationalContext;
import com.caucho.config.j2ee.BeanName;
import com.caucho.config.j2ee.BeanNameLiteral;
import com.caucho.config.reflect.AnnotatedMethodImpl;
import com.caucho.config.reflect.AnnotatedTypeUtil;
import com.caucho.config.reflect.BaseType;
import com.caucho.ejb.cfg.EjbLazyGenerator;
import com.caucho.ejb.inject.ProcessSessionBeanImpl;
import com.caucho.ejb.inject.SessionRegistrationBean;
import com.caucho.ejb.manager.EjbManager;
import com.caucho.ejb.server.AbstractEjbBeanManager;
import com.caucho.java.gen.JavaClassGenerator;
import com.caucho.loader.DynamicClassLoader;
import com.caucho.naming.Jndi;
import com.caucho.util.L10N;
/**
* Server container for a session bean.
*/
abstract public class AbstractSessionManager<X> extends AbstractEjbBeanManager<X> {
private static final L10N L = new L10N(AbstractSessionManager.class);
private final static Logger log
= Logger.getLogger(AbstractSessionManager.class.getName());
private EjbLazyGenerator<X> _lazyGenerator;
private Class<?> _proxyImplClass;
private HashMap<Class<?>, AbstractSessionContext<X,?>> _contextMap
= new HashMap<Class<?>, AbstractSessionContext<X,?>>();
private InjectManager _injectManager;
private Bean<X> _bean;
private String[] _declaredRoles;
public AbstractSessionManager(EjbManager manager,
String ejbName,
String moduleName,
AnnotatedType<X> rawAnnType,
AnnotatedType<X> annotatedType,
EjbLazyGenerator<X> lazyGenerator)
{
super(manager, ejbName, moduleName, rawAnnType, annotatedType);
_lazyGenerator = lazyGenerator;
DeclareRoles declareRoles
= annotatedType.getJavaClass().getAnnotation(DeclareRoles.class);
RolesAllowed rolesAllowed
= annotatedType.getJavaClass().getAnnotation(RolesAllowed.class);
if (declareRoles != null && rolesAllowed != null) {
_declaredRoles = new String[declareRoles.value().length +
rolesAllowed.value().length];
System.arraycopy(declareRoles.value(), 0,
_declaredRoles, 0,
declareRoles.value().length);
System.arraycopy(rolesAllowed.value(), 0,
_declaredRoles, declareRoles.value().length,
rolesAllowed.value().length);
}
else if (declareRoles != null) {
_declaredRoles = declareRoles.value();
}
else if (rolesAllowed != null) {
_declaredRoles = rolesAllowed.value();
}
}
@Override
protected String getType()
{
return "session:";
}
@Override
public Bean<X> getDeployBean()
{
return _bean;
}
public Class<?> getProxyImplClass()
{
return _proxyImplClass;
}
@Override
public InjectManager getInjectManager()
{
return _injectManager;
}
protected EjbLazyGenerator<X> getLazyGenerator()
{
return _lazyGenerator;
}
@Override
public ArrayList<AnnotatedType<? super X>> getLocalApi()
{
return _lazyGenerator.getLocalApi();
}
@Override
public ArrayList<AnnotatedType<? super X>> getRemoteApi()
{
return _lazyGenerator.getRemoteApi();
}
@Override
public AnnotatedType<X> getLocalBean()
{
return _lazyGenerator.getLocalBean();
}
@SuppressWarnings("unchecked")
protected <T> AbstractSessionContext<X,T> getSessionContext(Class<T> api)
{
return (AbstractSessionContext<X,T>) _contextMap.get(api);
}
/**
* Initialize the server during the config phase.
*/
@Override
public void init() throws Exception
{
Thread thread = Thread.currentThread();
ClassLoader oldLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(getClassLoader());
super.init();
_injectManager = InjectManager.create();
registerInjection();
for (AnnotatedType<? super X> localApi : _lazyGenerator.getLocalApi()) {
createContext(localApi.getJavaClass());
}
AnnotatedType<X> localBean = _lazyGenerator.getLocalBean();
if (localBean != null)
createContext(localBean.getJavaClass());
for (AnnotatedType<? super X> remoteApi : _lazyGenerator.getRemoteApi()) {
createContext(remoteApi.getJavaClass());
}
bindContext();
} finally {
thread.setContextClassLoader(oldLoader);
}
registerCdiBeans();
log.fine(this + " initialized");
}
@Override
public void bind()
{
Thread thread = Thread.currentThread();
ClassLoader loader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(getClassLoader());
boolean isAutoCompile = true;
if (_proxyImplClass == null) {
BeanGenerator<X> beanGen = createBeanGenerator();
String fullClassName = beanGen.getFullClassName();
JavaClassGenerator javaGen = getLazyGenerator().getJavaClassGenerator();
if (javaGen.preload(fullClassName) != null) {
}
else if (isAutoCompile) {
beanGen.introspect();
javaGen.generate(beanGen);
}
javaGen.compilePendingJava();
_proxyImplClass = generateProxyClass(fullClassName, javaGen);
}
for (AbstractSessionContext<X,?> cxt : _contextMap.values()) {
cxt.bind();
}
} catch (Exception e) {
throw ConfigException.create(e);
} finally {
thread.setContextClassLoader(loader);
}
}
/**
* Creates the bean generator for the session bean.
*/
protected BeanGenerator<X> createBeanGenerator()
{
throw new UnsupportedOperationException();
}
private <T> void createContext(Class<T> api)
{
if (_contextMap.get(api) != null)
throw new IllegalStateException(String.valueOf(api));
AbstractSessionContext<X,T> context = createSessionContext(api);
InjectManager injectManager = context.getInjectManager();
BeanBuilder<SessionContext> factory
= injectManager.createBeanFactory(SessionContext.class);
context.setDeclaredRoles(_declaredRoles);
// XXX: separate additions?
if (injectManager.getBeans(SessionContext.class).size() == 0)
injectManager.addBean(factory.singleton(context));
_contextMap.put(context.getApi(), context);
try {
String beanName = getAnnotatedType().getJavaClass().getName();
Jndi.bindDeep("java:comp/EJBContext", context);
Jndi.bindDeep("java:comp/" + beanName + "/ejbContext", context);
Jndi.bindDeep("java:comp/" + beanName + "/sessionContext", context);
} catch (Exception e) {
log.log(Level.FINER, e.toString(), e);
}
try {
TimerService timer = context.getTimerService();
BeanBuilder<TimerService> timerBuilder
= injectManager.createBeanFactory(TimerService.class);
if (injectManager.getBeans(TimerService.class).size() == 0)
injectManager.addBean(timerBuilder.singleton(timer));
} catch (Exception e) {
log.log(Level.ALL, e.toString(), e);
}
/*
if (_sessionContext == null) {
}
*/
}
private Class<?> generateProxyClass(String skeletonName,
JavaClassGenerator javaGen)
throws ClassNotFoundException
{
Class<?> proxyImplClass;
Class<?> ejbClass = getAnnotatedType().getJavaClass();
if (! isPublic(ejbClass)
&& (ejbClass.getClassLoader() instanceof DynamicClassLoader)) {
// ejb/1103
proxyImplClass = javaGen.loadClassParentLoader(skeletonName, ejbClass);
}
else {
proxyImplClass = javaGen.loadClass(skeletonName);
}
try {
Method method = proxyImplClass.getMethod("__caucho_getException");
RuntimeException exn = (RuntimeException) method.invoke(null);
if (exn != null)
throw exn;
} catch (RuntimeException exn) {
throw exn;
} catch (Exception exn) {
throw new RuntimeException(exn);
}
// contextImplClass.getDeclaredConstructors();
return proxyImplClass;
}
private boolean isPublic(Class<?> cl)
{
if (! Modifier.isPublic(cl.getModifiers()))
return false;
Class<?> superClass = cl.getSuperclass();
if (superClass != null && ! Modifier.isPublic(superClass.getModifiers()))
return false;
// ejb/5092 - CDI TCK
for (Class<?> ifClass : cl.getInterfaces()) {
if (! Modifier.isPublic(ifClass.getModifiers()))
return false;
}
return true;
}
@Override
public <T> T getLocalProxy(Class<T> api)
{
OwnerCreationalContext owner = new OwnerCreationalContext(null);
return getSessionContext(api).createProxy(owner);
}
protected <T> AbstractSessionContext<X,T>
createSessionContext(Class<T> api)
{
throw new UnsupportedOperationException(getClass().getName());
}
protected <T> SessionProxyFactory<T>
createProxyFactory(AbstractSessionContext<X,T> context)
{
try {
if (_proxyImplClass == null)
bind();
Class<?> []param = new Class[] { getClass(), getContextClass() };
Constructor<?> ctor = _proxyImplClass.getConstructor(param);
return (SessionProxyFactory<T>) ctor.newInstance(this, context);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException)
throw (RuntimeException) cause;
else
throw new IllegalStateException(cause);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
protected Class<?> getContextClass()
{
throw new UnsupportedOperationException(getClass().getName());
}
private void registerCdiBeans()
{
ArrayList<AnnotatedType<? super X>> localApiList = getLocalApi();
ArrayList<AnnotatedType<? super X>> remoteApiList = getRemoteApi();
AnnotatedType<X> rawAnnType = getRawAnnotatedType();
AnnotatedType<X> annType = getAnnotatedType();
AnnotatedType<X> extAnnType = createExternalAnnotatedType(annType, localApiList);
InjectManager moduleBeanManager = InjectManager.create();
ManagedBeanImpl<X> mBean
= new ManagedBeanImpl<X>(getInjectManager(), getAnnotatedType(), true);
mBean.introspect();
InjectionTarget<X> target = mBean.getInjectionTarget();
target = moduleBeanManager.processInjectionTarget(target, getRawAnnotatedType());
mBean.setInjectionTarget(target);
Class<?> baseApi = annType.getJavaClass();
Set<Type> apiList = new LinkedHashSet<Type>();
AnnotatedType<X> baseType = getLocalBean();
if (baseType != null) {
BaseType sourceApi = moduleBeanManager.createSourceBaseType(baseType.getBaseType());
apiList.addAll(sourceApi.getTypeClosure(moduleBeanManager));
}
if (localApiList != null) {
for (AnnotatedType<? super X> api : localApiList) {
baseApi = api.getJavaClass();
BaseType sourceApi = moduleBeanManager.createSourceBaseType(api.getJavaClass());
apiList.addAll(sourceApi.getTypeClosure(moduleBeanManager));
}
}
apiList.add(Object.class);
// ioc/024p
/*
if (remoteApiList != null) {
for (AnnotatedType<? super X> api : remoteApiList) {
if (baseApi == null)
baseApi = api.getJavaClass();
BaseType sourceApi = moduleBeanManager.createSourceBaseType(api.getJavaClass());
apiList.addAll(sourceApi.getTypeClosure(moduleBeanManager));
}
}
*/
if (baseApi == null)
throw new NullPointerException();
_bean = (Bean<X>) createBean(mBean, baseApi, apiList, extAnnType);
// CDI TCK requires the rawAnnType, not the processed one
ProcessSessionBeanImpl process
= new ProcessSessionBeanImpl(moduleBeanManager,
_bean,
rawAnnType,
getEJBName(),
getSessionBeanType());
moduleBeanManager.addBean(_bean, process);
if (! moduleBeanManager.isSpecialized(annType.getJavaClass())) {
moduleBeanManager.addProduces(_bean, extAnnType);
}
for (AnnotatedType<?> localApi : getLocalApi()) {
registerLocalSession(moduleBeanManager, localApi.getJavaClass());
}
for (AnnotatedType<?> remoteApi : getRemoteApi()) {
registerLocalSession(moduleBeanManager, remoteApi.getJavaClass());
}
if (getLocalBean() != null) {
registerLocalSession(moduleBeanManager, getLocalBean().getJavaClass());
}
}
private AnnotatedType<X>
createExternalAnnotatedType(AnnotatedType<X> baseType,
ArrayList<AnnotatedType<? super X>> apiList)
{
ExtAnnotatedType<X> extAnnType = new ExtAnnotatedType<X>(baseType);
for (AnnotatedField<? super X> field : baseType.getFields()) {
if (field.isStatic())
extAnnType.addField(field);
}
for (AnnotatedMethod<? super X> method : baseType.getMethods()) {
AnnotatedMethod<? super X> extMethod = mergeMethod(method, apiList);
if (extMethod != null)
extAnnType.addMethod(extMethod);
else if (method.isAnnotationPresent(Produces.class)
&& ! baseType.isAnnotationPresent(Specializes.class)) {
// TCK: conflict
// ioc/07fa, ioc/07a4
throw new ConfigException(L.l("{0}.{1} is an invalid @Produces EJB method because the method is not in a @Local interface.",
method.getDeclaringType().getJavaClass().getName(),
method.getJavaMember().getName()));
}
else if (isDisposes(method)) {
throw new ConfigException(L.l("{0}.{1} is an invalid @Disposes EJB method because the method is not in a @Local interface.",
method.getDeclaringType().getJavaClass().getName(),
method.getJavaMember().getName()));
}
}
return extAnnType;
}
private boolean isDisposes(AnnotatedMethod<? super X> method)
{
for (AnnotatedParameter<? super X> param : method.getParameters()) {
if (param.isAnnotationPresent(Disposes.class))
return true;
}
return false;
}
private AnnotatedMethod<? super X>
mergeMethod(AnnotatedMethod<? super X> method,
ArrayList<AnnotatedType<? super X>> apiList)
{
// ioc/07g3
if (apiList.size() == 0)
return method;
for (AnnotatedType<? super X> api : apiList) {
AnnotatedMethod<? super X> apiMethod
= AnnotatedTypeUtil.findMethod(api, method);
if (apiMethod != null) {
AnnotatedMethodImpl<? super X> extMethod
= new AnnotatedMethodImpl(apiMethod.getDeclaringType(),
method,
apiMethod.getJavaMember(),
toArray(apiMethod.getAnnotations()),
null);
return extMethod;
}
}
return null;
}
private Annotation []toArray(Set<Annotation> annSet)
{
Annotation []ann = new Annotation[annSet.size()];
annSet.toArray(ann);
return ann;
}
private <T> void registerLocalSession(InjectManager beanManager,
Class<T> localApi)
{
AbstractSessionContext<X,T> context = getSessionContext(localApi);
BeanName beanName = new BeanNameLiteral(getEJBName());
SessionRegistrationBean<X,T> regBean
= new SessionRegistrationBean<X,T>(beanManager, context, _bean, beanName);
beanManager.addBeanImpl(regBean, regBean.getAnnotated());
}
protected Bean<X> getBean()
{
return _bean;
}
abstract protected <T> Bean<T>
createBean(ManagedBeanImpl<X> mBean,
Class<T> api,
Set<Type> apiList,
AnnotatedType<X> extAnnType);
protected SessionBeanType getSessionBeanType()
{
return SessionBeanType.STATELESS;
}
@Override
public void destroy()
{
for (AbstractSessionContext<X,?> context : _contextMap.values()) {
try {
context.destroy();
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
}
}
}
}