/* * 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.cfg; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.sql.DataSourceDefinition; import javax.annotation.sql.DataSourceDefinitions; import javax.ejb.ApplicationException; import javax.ejb.EJBs; import javax.ejb.MessageDriven; import javax.ejb.Singleton; import javax.ejb.Stateful; import javax.ejb.Stateless; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.InjectionTarget; import com.caucho.config.ConfigException; import com.caucho.config.gen.ApplicationExceptionConfig; import com.caucho.config.inject.InjectManager; import com.caucho.config.types.FileSetType; import com.caucho.ejb.manager.EjbManager; import com.caucho.ejb.server.AbstractEjbBeanManager; import com.caucho.ejb.util.AppExceptionItem; import com.caucho.java.gen.JavaClassGenerator; import com.caucho.jms.JmsMessageListener; import com.caucho.util.L10N; import com.caucho.vfs.Path; /** * Manages the EJB configuration files. */ public class EjbConfig { private static final L10N L = new L10N(EjbConfig.class); protected final EjbManager _ejbManager; private ArrayList<FileSetType> _fileSetList = new ArrayList<FileSetType>(); private HashMap<String,EjbBean<?>> _cfgBeans = new HashMap<String,EjbBean<?>>(); private HashSet<Class<?>> _beanSet = new HashSet<Class<?>>(); private ArrayList<EjbBean<?>> _pendingBeans = new ArrayList<EjbBean<?>>(); private ArrayList<EjbBean<?>> _deployingBeans = new ArrayList<EjbBean<?>>(); private ArrayList<EjbBeanConfigProxy> _proxyList = new ArrayList<EjbBeanConfigProxy>(); private ArrayList<FunctionSignature> _functions = new ArrayList<FunctionSignature>(); private HashMap<String, MessageDestination> _messageDestinations; private ArrayList<Interceptor> _cfgInterceptors = new ArrayList<Interceptor>(); private ArrayList<InterceptorBinding> _cfgInterceptorBindings = new ArrayList<InterceptorBinding>(); private HashMap<Class<?>,ApplicationExceptionConfig> _appExceptionConfig = new HashMap<Class<?>,ApplicationExceptionConfig>(); private ConcurrentHashMap<Class<?>,AppExceptionItem> _appExceptionMap = new ConcurrentHashMap<Class<?>,AppExceptionItem>(); private ArrayList<AnnotatedType<?>> _bindTypes = new ArrayList<AnnotatedType<?>>(); public EjbConfig(EjbManager ejbContainer) { _ejbManager = ejbContainer; } /** * Adds a path for an EJB config file to the config list. */ public void addEjbPath(Path path) throws ConfigException { throw new UnsupportedOperationException(); } public void addConfigProxy(EjbBeanConfigProxy proxy) { _proxyList.add(proxy); _beanSet.add(proxy.getEjbClass()); } /** * Returns the schema name. */ public String getSchema() { return "com/caucho/ejb/cfg/resin-ejb.rnc"; } /** * Returns the EJB manager. */ public EjbManager getEjbContainer() { return _ejbManager; } /** * Returns the cfg bean with the given name. */ public EjbBean<?> getBeanConfig(String name) { assert name != null; return _cfgBeans.get(name); } /** * Sets the cfg bean with the given name. */ public void setBeanConfig(String name, EjbBean<?> bean) { if (name == null || bean == null) throw new NullPointerException(); EjbBean<?> oldBean = _cfgBeans.get(name); if (oldBean == bean) return; else if (oldBean != null) { throw new IllegalStateException(L.l("{0}: duplicate bean '{1}' old ejb-class={2} new ejb-class={3}", this, name, oldBean, // .getEJBClass().getName())); bean)); // .getEJBClass().getName())); } _pendingBeans.add(bean); _cfgBeans.put(name, bean); _beanSet.add(bean.getEJBClass()); } /** * Returns the interceptor with the given class name. */ public Interceptor getInterceptor(String className) { assert className != null; for (Interceptor interceptor : _cfgInterceptors) { if (interceptor.getInterceptorClass().equals(className)) return interceptor; } return null; } /** * Adds an interceptor. */ public void addInterceptor(Interceptor interceptor) { if (interceptor == null) throw new NullPointerException(); _cfgInterceptors.add(interceptor); } /** * Returns the interceptor bindings for a given ejb name. */ public ArrayList<InterceptorBinding> getInterceptorBinding(String ejbName, boolean isExcludeDefault) { assert ejbName != null; ArrayList<InterceptorBinding> bindings = new ArrayList<InterceptorBinding>(); for (InterceptorBinding binding : _cfgInterceptorBindings) { if (binding.getEjbName().equals(ejbName)) bindings.add(binding); else if (binding.getEjbName().equals("*") && ! isExcludeDefault) { bindings.add(binding); } } return bindings; } /** * Adds an application exception. */ public void addApplicationException(ApplicationExceptionConfig applicationException) { Class<?> appExnClass = applicationException.getExceptionClass(); _appExceptionConfig.put(appExnClass, applicationException); } /** * Returns the application exceptions. */ public AppExceptionItem getApplicationException(Class<?> exn, boolean isSystem) { AppExceptionItem appExn = _appExceptionMap.get(exn); if (appExn == null) { appExn = createApplicationException(exn, isSystem); _appExceptionMap.put(exn, appExn); } return appExn; } private AppExceptionItem createApplicationException(Class<?> exn, boolean isSystem) { if (exn == Error.class || exn == RuntimeException.class) return new AppExceptionItem(false, true, true); else if (exn == Exception.class) return new AppExceptionItem(true, false, true); ApplicationExceptionConfig cfg = _appExceptionConfig.get(exn); if (cfg != null) { return new AppExceptionItem(true, cfg.isRollback(), cfg.isInherited()); } ApplicationException appExn = exn.getAnnotation(ApplicationException.class); if (appExn != null) { // ejb/1276 return new AppExceptionItem(true, appExn.rollback(), appExn.inherited()); } AppExceptionItem parentItem = getApplicationException(exn.getSuperclass(), isSystem); if (parentItem.isInherited()) return parentItem; else if (isSystem) return new AppExceptionItem(false, true, true); else return new AppExceptionItem(true, false, true); } /** * Binds an interceptor to an ejb. */ public void addInterceptorBinding(InterceptorBinding interceptorBinding) { _cfgInterceptorBindings.add(interceptorBinding); } /** * Adds the message destination mapping */ public void addMessageDestination(MessageDestination messageDestination) { if (_messageDestinations == null) _messageDestinations = new HashMap<String, MessageDestination>(); String name = messageDestination.getMessageDestinationName(); _messageDestinations.put(name, messageDestination); } public MessageDestination getMessageDestination(String name) { if (_messageDestinations == null) return null; return _messageDestinations.get(name); } public boolean isConfiguredBean(Class<?> beanType) { return _beanSet.contains(beanType); } public <X> void addAnnotatedType(AnnotatedType<X> rawAnnType, AnnotatedType<X> annType, InjectionTarget<X> injectTarget, String moduleName) { try { Class<?> type = annType.getJavaClass(); if (findBeanByType(type) != null) return; if (annType.isAnnotationPresent(Stateless.class)) { EjbStatelessBean<X> bean = new EjbStatelessBean<X>(this, rawAnnType, annType, moduleName); bean.setInjectionTarget(injectTarget); Stateless stateless = annType.getAnnotation(Stateless.class); if (! "".equals(stateless.name()) && stateless.name() != null) bean.setEJBName(stateless.name()); setBeanConfig(bean.getEJBName(), bean); } else if (annType.isAnnotationPresent(Stateful.class)) { EjbStatefulBean<X> bean = new EjbStatefulBean<X>(this, rawAnnType, annType, moduleName); bean.setInjectionTarget(injectTarget); Stateful stateful = annType.getAnnotation(Stateful.class); if (! "".equals(stateful.name()) && stateful.name() != null) bean.setEJBName(stateful.name()); setBeanConfig(bean.getEJBName(), bean); } else if (annType.isAnnotationPresent(Singleton.class)) { EjbSingletonBean<X> bean = new EjbSingletonBean<X>(this, rawAnnType, annType, moduleName); bean.setInjectionTarget(injectTarget); Singleton singleton = annType.getAnnotation(Singleton.class); if (! "".equals(singleton.name()) && singleton.name() != null) bean.setEJBName(singleton.name()); setBeanConfig(bean.getEJBName(), bean); } else if (annType.isAnnotationPresent(MessageDriven.class)) { EjbMessageBean<X> bean = new EjbMessageBean<X>(this, rawAnnType, annType, moduleName); bean.setInjectionTarget(injectTarget); setBeanConfig(bean.getEJBName(), bean); } else if (annType.isAnnotationPresent(JmsMessageListener.class)) { JmsMessageListener listener = annType.getAnnotation(JmsMessageListener.class); EjbMessageBean<X> bean = new EjbMessageBean<X>(this, rawAnnType, annType, listener.destination()); bean.setInjectionTarget(injectTarget); setBeanConfig(bean.getEJBName(), bean); } EJBs ejbs = annType.getAnnotation(EJBs.class); if (ejbs != null) { _bindTypes.add(annType); } // ejb/3042 DataSourceDefinition dbDef = annType.getAnnotation(DataSourceDefinition.class); if (dbDef != null) { _bindTypes.add(annType); } DataSourceDefinitions dbDefs = annType.getAnnotation(DataSourceDefinitions.class); if (dbDefs != null) { _bindTypes.add(annType); } } catch (ConfigException e) { throw e; } catch (Exception e) { throw ConfigException.create(e); } } /** * Finds an entity bean by its abstract schema. */ @SuppressWarnings("unchecked") public <X> EjbBean<X> findBeanByType(Class<X> type) { for (EjbBean<?> bean : _cfgBeans.values()) { Class<?> cl = bean.getEJBClass(); // ejb/0j03 if (cl != null && cl.getName().equals(type.getName())) return (EjbBean<X>) bean; } return null; } /** * Adds a function. */ public void addFunction(FunctionSignature sig, String sql) { _functions.add(sig); } /** * Gets the function list. */ public ArrayList<FunctionSignature> getFunctions() { return _functions; } /** * Configures the pending beans. */ public void configure() throws ConfigException { findConfigurationFiles(); try { for (EjbBeanConfigProxy configProxy : _proxyList) { configProxy.configure(); } _proxyList.clear(); ArrayList<EjbBean<?>> beanConfig = new ArrayList<EjbBean<?>>(_pendingBeans); _pendingBeans.clear(); _deployingBeans.addAll(beanConfig); /* for (EjbBean<?> bean : beanConfig) { bean.init(); } */ } catch (RuntimeException e) { throw e; } catch (Exception e) { throw ConfigException.create(e); } } /** * Configures the pending beans. */ private void findConfigurationFiles() throws ConfigException { for (FileSetType fileSet : _fileSetList) { for (Path path : fileSet.getPaths()) { addEjbPath(path); } } } /** * Configures the pending beans. */ public void deploy() throws ConfigException { try { ClassLoader parentLoader = _ejbManager.getClassLoader(); Path workDir = _ejbManager.getWorkDir(); JavaClassGenerator javaGen = new JavaClassGenerator(); javaGen.setWorkDir(workDir); javaGen.setParentLoader(parentLoader); ArrayList<EjbBean<?>> deployingBeans = new ArrayList<EjbBean<?>>(_deployingBeans); _deployingBeans.clear(); deployBeans(deployingBeans, javaGen); deployBindings(_bindTypes); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw ConfigException.create(e); } } /** * Configures the pending beans. */ public void deployBeans(ArrayList<EjbBean<?>> beanConfig, JavaClassGenerator javaGen) throws Exception { Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { thread.setContextClassLoader(_ejbManager.getClassLoader()); // ejb/0g1c, ejb/0f68, ejb/0f69 ArrayList<EjbBean<?>> beanList = new ArrayList<EjbBean<?>>(); for (EjbBean<?> bean : beanConfig) { if (beanList.contains(bean)) continue; bean.init(); deployBean(beanConfig, javaGen, beanList, bean); } } finally { thread.setContextClassLoader(oldLoader); } } private <X> void deployBean(ArrayList<EjbBean<?>> beanConfig, JavaClassGenerator javaGen, ArrayList<EjbBean<?>> beanList, EjbBean<X> bean) throws Exception { Thread thread = Thread.currentThread(); EjbLazyGenerator<X> lazyGenerator = new EjbLazyGenerator<X>(bean.getAnnotatedType(), javaGen, bean.getLocalList(), bean.getLocalBean(), bean.getRemoteList()); AbstractEjbBeanManager<X> server = initBean(bean, lazyGenerator); _ejbManager.addServer(server); ArrayList<String> dependList = bean.getBeanDependList(); for (String depend : dependList) { for (EjbBean<?> b : beanConfig) { if (bean == b) continue; // XXX: what test case is this for? if (depend.equals(b.getEJBName())) { beanList.add(b); /* AbstractEjbBeanManager<?> dependServer = initBean(b, lazyGenerator); initResources(b, dependServer); */ thread.setContextClassLoader(server.getClassLoader()); } } } // XXX: 4.0.8 timing issues // initResources(bean, server); } private <X> AbstractEjbBeanManager<X> initBean(EjbBean<X> bean, EjbLazyGenerator<X> lazyGenerator) throws Exception { AbstractEjbBeanManager<X> server = bean.deployServer(_ejbManager, lazyGenerator); server.init(); return server; } private void deployBindings(ArrayList<AnnotatedType<?>> bindTypes) { InjectManager cdiManager = _ejbManager.getInjectManager(); for (AnnotatedType<?> annType : bindTypes) { // ioc/123q cdiManager.getInjectionPointHandler(EJBs.class).introspectType(annType); cdiManager.getInjectionPointHandler(DataSourceDefinition.class).introspectType(annType); cdiManager.getInjectionPointHandler(DataSourceDefinitions.class).introspectType(annType); } } @Override public String toString() { String id = _ejbManager.getClassLoader().getId(); return getClass().getSimpleName() + "[" + id + "]"; } }