/* * 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.manager; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.InjectionTarget; import javax.jms.ConnectionFactory; import com.caucho.amber.manager.AmberContainer; import com.caucho.config.ConfigException; import com.caucho.config.inject.InjectManager; import com.caucho.ejb.cfg.EjbConfigManager; import com.caucho.ejb.cfg.EjbRootConfig; import com.caucho.ejb.protocol.EjbProtocolManager; import com.caucho.ejb.server.AbstractEjbBeanManager; import com.caucho.ejb.util.AppExceptionItem; import com.caucho.java.WorkDir; import com.caucho.loader.Environment; import com.caucho.loader.EnvironmentClassLoader; import com.caucho.loader.EnvironmentListener; import com.caucho.loader.EnvironmentLocal; import com.caucho.loader.enhancer.ScanClass; import com.caucho.loader.enhancer.ScanListener; import com.caucho.util.CharBuffer; import com.caucho.vfs.JarPath; import com.caucho.vfs.Path; /** * Environment-based container. */ public class EjbManager implements ScanListener, EnvironmentListener { private static final Logger log = Logger.getLogger(EjbManager.class .getName()); private static final EnvironmentLocal<EjbManager> _localContainer = new EnvironmentLocal<EjbManager>(); private static final EnvironmentLocal<Boolean> _localScanAll = new EnvironmentLocal<Boolean>(); private ClassLoader _globalClassLoader; private final EnvironmentClassLoader _classLoader; private final ClassLoader _tempClassLoader; private final EjbManager _parentContainer; private final EjbConfigManager _configManager; private final EjbProtocolManager _protocolManager; private final HashSet<String> _ejbUrls = new HashSet<String>(); private InjectManager _cdiManager; // the exact list of root to scan - used by EJBContainer private ArrayList<Path> _scannableRoots = null; // // configuration // private boolean _isAutoCompile = true; private Path _workDir; private ConnectionFactory _jmsConnectionFactory; private int _messageConsumerMax = 5; // // active servers // private final ArrayList<AbstractEjbBeanManager<?>> _serverList = new ArrayList<AbstractEjbBeanManager<?>>(); private final AtomicBoolean _isConfig = new AtomicBoolean(); private EjbManager(ClassLoader loader) { _parentContainer = _localContainer.get(loader); _classLoader = Environment.getEnvironmentClassLoader(loader); _tempClassLoader = _classLoader.getNewTempClassLoader(); _localContainer.set(this, _classLoader); if (_parentContainer != null) copyContainerDefaults(_parentContainer); // _ejbAdmin = new EJBAdmin(this); _protocolManager = new EjbProtocolManager(this); _configManager = new EjbConfigManager(this); // _workDir = WorkDir.getLocalWorkDir().lookup("ejb"); _cdiManager = InjectManager.getCurrent(_classLoader); _classLoader.addScanListener(this); Environment.addEnvironmentListener(this); EjbModule.create("default"); } /** * Returns the local container. */ public static EjbManager create() { return create(Thread.currentThread().getContextClassLoader()); } /** * Returns the local container. */ public static EjbManager create(ClassLoader loader) { synchronized (_localContainer) { EjbManager container = _localContainer.getLevel(loader); if (container == null) { Boolean ejbManager = null; ejbManager = (Boolean) Environment.getAttribute("ejb.manager", loader); if (ejbManager == null || Boolean.TRUE.equals(ejbManager)) container = new EjbManager(loader); else container = _localContainer.get(loader); _localContainer.set(container, loader); } return container; } } /** * Returns the local container. */ public static EjbManager getCurrent() { return getCurrent(Thread.currentThread().getContextClassLoader()); } /** * Returns the current environment container. */ public static EjbManager getCurrent(ClassLoader loader) { synchronized (_localContainer) { return _localContainer.get(loader); } } public static void setScanAll() { _localScanAll.set(true); } public void setGlobalClassLoader(ClassLoader globalClassLoader) { _globalClassLoader = globalClassLoader; } /** * Returns the parent loader */ public EnvironmentClassLoader getClassLoader() { return _classLoader; } public ClassLoader getGlobalClassLoader() { if (_globalClassLoader != null) return _globalClassLoader; else return _classLoader; } /** * Returns the introspection class loader */ public ClassLoader getIntrospectionClassLoader() { return _tempClassLoader; } /** * Returns the configuration manager. */ public EjbConfigManager getConfigManager() { return _configManager; } public EjbManager getParent() { return _parentContainer; } /** * Returns the protocol manager. */ public EjbProtocolManager getProtocolManager() { return _protocolManager; } public InjectManager getInjectManager() { return _cdiManager; } /** * true if beans should be auto-compiled */ public void setAutoCompile(boolean isAutoCompile) { _isAutoCompile = isAutoCompile; } /** * true if beans should be auto-compiled */ public boolean isAutoCompile() { return _isAutoCompile; } /** * The work directory for EJB-generated files */ public void setWorkDir(Path workDir) { _workDir = workDir; } /** * The work directory for EJB-generated files */ public Path getWorkDir() { if (_workDir != null) return _workDir; else return WorkDir.getLocalWorkDir().lookup("ejb"); } /** * The JMS connection factory for the container. */ public void setJmsConnectionFactory(ConnectionFactory factory) { _jmsConnectionFactory = factory; } /** * Sets the consumer maximum for the container. */ public void setMessageConsumerMax(int consumerMax) { _messageConsumerMax = consumerMax; } /** * The consumer maximum for the container. */ public int getMessageConsumerMax() { return _messageConsumerMax; } /** * Copy defaults from the parent container when first created. */ private void copyContainerDefaults(EjbManager parent) { _isAutoCompile = parent._isAutoCompile; _jmsConnectionFactory = parent._jmsConnectionFactory; _messageConsumerMax = parent._messageConsumerMax; } // // Bean configuration and management // public boolean isConfiguredBean(Class<?> beanType) { return _configManager.isConfiguredBean(beanType); } public <T> void createBean(AnnotatedType<T> type, InjectionTarget<T> injectionTarget) { // XXX moduleName _configManager.addAnnotatedType(type, type, injectionTarget, ""); } // // AbstractServer management // /** * Adds a server. */ public void addServer(AbstractEjbBeanManager<?> server) { _serverList.add(server); getProtocolManager().addServer(server); } public AbstractEjbBeanManager<?> getServerByEjbName(String name) { for (AbstractEjbBeanManager<?> server : _serverList) { if (server.getEJBName().equals(name)) return server; } if (_parentContainer != null) return _parentContainer.getServerByEjbName(name); else return null; } /** * Since EJB doesn't bytecode enhance, it's priority 1 */ @Override public int getScanPriority() { return 1; } /** * Adds a root URL */ public void configureRootPath(Path root) { if (root.getURL().endsWith(".jar")) root = JarPath.create(root); // XXX: ejb/0fbn Path ejbJar = root.lookup("META-INF/ejb-jar.xml"); if (ejbJar.canRead()) { getConfigManager().configureRootPath(root); } else if (root.getURL().endsWith("WEB-INF/classes")) { ejbJar = root.lookup("../ejb-jar.xml"); if (ejbJar.canRead()) { getConfigManager().configureRootPath(root); } } _ejbUrls.add(root.getURL()); } public void setScannableRoots(ArrayList<Path> roots) { _scannableRoots = roots; } /** * Returns true if the root is a valid scannable root. */ @Override public boolean isRootScannable(Path root, String packageRoot) { Path scanRoot = root; if (packageRoot != null) scanRoot = scanRoot.lookup(packageRoot.replace('.', '/')); if (_scannableRoots == null) { if (! Boolean.TRUE.equals(_localScanAll.get())) { if (! scanRoot.lookup("META-INF/ejb-jar.xml").canRead()) { return false; } } if (_ejbUrls.contains(root.getURL())) { return false; } } else { Path path = scanRoot; if (root instanceof JarPath) path = ((JarPath) root).getContainer(); if (! _scannableRoots.contains(path)) return false; } if (log.isLoggable(Level.FINE)) log.fine("EJB scanning '" + root + "'"); EjbRootConfig context = _configManager.createRootConfig(scanRoot); if (context.isScanComplete()) return false; else { context.setScanComplete(true); return true; } } @Override public ScanClass scanClass(Path root, String packageRoot, String className, int modifiers) { if (Modifier.isInterface(modifiers)) return null; /* else if (Modifier.isAbstract(modifiers)) return null; */ else return new EjbScanClass(root, className, this); } @Override public boolean isScanMatchAnnotation(CharBuffer annotationName) { if (annotationName.matches("javax.ejb.Stateless")) { return true; } if (annotationName.matches("javax.ejb.Singleton")) { return true; } else if (annotationName.matches("javax.ejb.Stateful")) { return true; } else if (annotationName.matches("javax.ejb.MessageDriven")) { return true; } else if (annotationName.matches("javax.annotation.ManagedBean")) { return true; } else return false; } /** * Callback to note the class matches */ public void classMatchEvent(EnvironmentClassLoader loader, Path root, String className) { EjbRootConfig config = _configManager.createRootConfig(root); config.addClassName(className); } void addScanClass(Path root, String className) { EjbRootConfig config = _configManager.createRootConfig(root); config.addClassName(className); } // // app exception /** * Returns the configuration for a system exception. */ public AppExceptionItem getSystemException(Class<?> exceptionClass) { return _configManager.getApplicationException(exceptionClass, true); } /** * Returns the configuration for an application exception. */ public AppExceptionItem getApplicationException(Class<?> exceptionClass) { return _configManager.getApplicationException(exceptionClass, false); } // // lifecycle methods // public void init() { } private void config() { if (_isConfig.getAndSet(true)) return; _configManager.start(); } private void bind() { config(); _cdiManager.bind(); for (AbstractEjbBeanManager<?> manager : sortManagers()) { manager.bind(); } } private ArrayList<AbstractEjbBeanManager<?>> sortManagers() { ArrayList<AbstractEjbBeanManager<?>> serverList = new ArrayList<AbstractEjbBeanManager<?>>(_serverList); ArrayList<AbstractEjbBeanManager<?>> bindList = new ArrayList<AbstractEjbBeanManager<?>>(); AbstractEjbBeanManager<?> server; while ((server = nextBindServer(serverList, bindList)) != null) { } return bindList; } private AbstractEjbBeanManager<?> nextBindServer(ArrayList<AbstractEjbBeanManager<?>> serverList, ArrayList<AbstractEjbBeanManager<?>> bindList) { if (serverList.size() == 0) return null; for (AbstractEjbBeanManager<?> server : serverList) { if (server.isDependValid(bindList)) { serverList.remove(server); bindList.add(server); return server; } } AbstractEjbBeanManager<?> server = serverList.remove(0); bindList.add(server); return server; } public void start() throws ConfigException { try { // AmberContainer.create().start(); bind(); // ejb/4200 Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); for (AbstractEjbBeanManager<?> server : sortManagers()) { try { thread.setContextClassLoader(server.getClassLoader()); server.start(); } finally { thread.setContextClassLoader(oldLoader); } } // AmberContainer.create().start(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw ConfigException.create(e); } } /** * Closes the container. */ public void destroy() { /* * if (! _lifecycle.toDestroy()) return; */ try { ArrayList<AbstractEjbBeanManager<?>> servers; servers = new ArrayList<AbstractEjbBeanManager<?>>(_serverList); _serverList.clear(); // only purpose of the sort is to make the qa order consistent Collections.sort(servers, new ServerCmp()); for (AbstractEjbBeanManager<?> server : servers) { try { getProtocolManager().removeServer(server); } catch (Throwable e) { log.log(Level.WARNING, e.toString(), e); } } for (AbstractEjbBeanManager<?> server : servers) { try { server.destroy(); } catch (Throwable e) { log.log(Level.WARNING, e.toString(), e); } } } catch (Throwable e) { log.log(Level.WARNING, e.toString(), e); } } /** * Handles the case where the environment is configuring */ public void environmentConfigure(EnvironmentClassLoader loader) { config(); } /** * Handles the case where the environment is configuring */ @Override public void environmentBind(EnvironmentClassLoader loader) { bind(); } /** * Handles the case where the environment is starting (after init). */ public void environmentStart(EnvironmentClassLoader loader) { start(); } /** * Handles the case where the environment is stopping */ public void environmentStop(EnvironmentClassLoader loader) { destroy(); } public String toString() { return getClass().getSimpleName() + "[" + _classLoader + "]"; } /** * Sorts the servers so they can be destroyed in a consistent order. (To make * QA sane.) */ static class ServerCmp implements Comparator<AbstractEjbBeanManager<?>> { public int compare(AbstractEjbBeanManager<?> a, AbstractEjbBeanManager<?> b) { return a.getEJBName().compareTo(b.getEJBName()); } } }