| /* |
| * 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.openejb.cdi; |
| |
| import org.apache.openejb.AppContext; |
| import org.apache.openejb.BeanContext; |
| import org.apache.openejb.OpenEJBRuntimeException; |
| import org.apache.openejb.assembler.classic.Assembler; |
| import org.apache.openejb.loader.SystemInstance; |
| import org.apache.openejb.util.LogCategory; |
| import org.apache.openejb.util.Logger; |
| import org.apache.webbeans.component.InjectionPointBean; |
| import org.apache.webbeans.component.NewBean; |
| import org.apache.webbeans.config.OpenWebBeansConfiguration; |
| import org.apache.webbeans.config.WebBeansContext; |
| import org.apache.webbeans.config.WebBeansFinder; |
| import org.apache.webbeans.container.BeanManagerImpl; |
| import org.apache.webbeans.ejb.common.component.EjbBeanCreatorImpl; |
| import org.apache.webbeans.ejb.common.util.EjbUtility; |
| import org.apache.webbeans.intercept.InterceptorData; |
| import org.apache.webbeans.portable.events.ExtensionLoader; |
| import org.apache.webbeans.portable.events.ProcessAnnotatedTypeImpl; |
| import org.apache.webbeans.portable.events.discovery.BeforeShutdownImpl; |
| import org.apache.webbeans.spi.ContainerLifecycle; |
| import org.apache.webbeans.spi.ContextsService; |
| import org.apache.webbeans.spi.JNDIService; |
| import org.apache.webbeans.spi.ResourceInjectionService; |
| import org.apache.webbeans.spi.ScannerService; |
| import org.apache.webbeans.spi.adaptor.ELAdaptor; |
| import org.apache.webbeans.util.WebBeansConstants; |
| import org.apache.webbeans.util.WebBeansUtil; |
| import org.apache.webbeans.xml.WebBeansXMLConfigurator; |
| |
| import javax.el.ELResolver; |
| import javax.enterprise.inject.Specializes; |
| import javax.enterprise.inject.spi.AnnotatedType; |
| import javax.enterprise.inject.spi.BeanManager; |
| import javax.servlet.ServletContext; |
| import javax.servlet.ServletContextEvent; |
| import javax.servlet.jsp.JspApplicationContext; |
| import javax.servlet.jsp.JspFactory; |
| import java.io.IOException; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.ThreadFactory; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * @version $Rev:$ $Date:$ |
| */ |
| public class OpenEJBLifecycle implements ContainerLifecycle { |
| |
| //Logger instance |
| private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB_CDI, OpenEJBLifecycle.class); |
| |
| public static final String OPENEJB_CDI_SKIP_CLASS_NOT_FOUND = "openejb.cdi.skip-class-not-found"; |
| |
| /**Discover bean classes*/ |
| protected ScannerService scannerService; |
| |
| protected final ContextsService contextsService; |
| |
| private final boolean skipClassNotFoundError; |
| |
| /**Deploy discovered beans*/ |
| private final BeansDeployer deployer; |
| |
| /**XML discovery. */ |
| //XML discovery is removed from the specification. It is here for next revisions of spec. |
| private final WebBeansXMLConfigurator xmlDeployer; |
| |
| /**Using for lookup operations*/ |
| private final JNDIService jndiService; |
| |
| /**Root container.*/ |
| private final BeanManagerImpl beanManager; |
| private final WebBeansContext webBeansContext; |
| /**Manages unused conversations*/ |
| private ScheduledExecutorService service = null; |
| |
| //TODO make sure this isn't used and remove it |
| public OpenEJBLifecycle() { |
| this(WebBeansContext.currentInstance()); |
| } |
| |
| public OpenEJBLifecycle(WebBeansContext webBeansContext) |
| { |
| this.webBeansContext = webBeansContext; |
| beforeInitApplication(null); |
| |
| this.beanManager = webBeansContext.getBeanManagerImpl(); |
| this.xmlDeployer = new WebBeansXMLConfigurator(); |
| this.deployer = new BeansDeployer(this.xmlDeployer, webBeansContext); |
| this.jndiService = webBeansContext.getService(JNDIService.class); |
| this.beanManager.setXMLConfigurator(this.xmlDeployer); |
| this.scannerService = webBeansContext.getScannerService(); |
| this.contextsService = webBeansContext.getContextsService(); |
| |
| this.skipClassNotFoundError = SystemInstance.get().getOptions().get(OPENEJB_CDI_SKIP_CLASS_NOT_FOUND, false); |
| |
| initApplication(null); |
| } |
| |
| @Override |
| public BeanManager getBeanManager() |
| { |
| return this.beanManager; |
| } |
| |
| @Override |
| public void startApplication(Object startupObject) |
| { |
| if (startupObject instanceof ServletContextEvent) { |
| startServletContext((ServletContext) getServletContext(startupObject)); // TODO: check it is relevant |
| return; |
| } else if (!(startupObject instanceof StartupObject)) { |
| logger.debug("startupObject is not of StartupObject type; ignored"); |
| return; |
| } |
| |
| StartupObject stuff = (StartupObject) startupObject; |
| final ClassLoader oldCl = Thread.currentThread().getContextClassLoader(); |
| |
| // Initalize Application Context |
| logger.info("OpenWebBeans Container is starting..."); |
| |
| long begin = System.currentTimeMillis(); |
| |
| try { |
| Thread.currentThread().setContextClassLoader(stuff.getClassLoader()); |
| |
| //Before Start |
| beforeStartApplication(startupObject); |
| |
| |
| //Load all plugins |
| webBeansContext.getPluginLoader().startUp(); |
| |
| //Get Plugin |
| CdiPlugin cdiPlugin = (CdiPlugin) webBeansContext.getPluginLoader().getEjbPlugin(); |
| |
| final AppContext appContext = stuff.getAppContext(); |
| if (stuff.getWebContext() == null) { |
| appContext.setWebBeansContext(webBeansContext); |
| } |
| |
| cdiPlugin.setClassLoader(stuff.getClassLoader()); |
| cdiPlugin.setWebBeansContext(webBeansContext); |
| cdiPlugin.startup(); |
| |
| //Configure EJB Deployments |
| cdiPlugin.configureDeployments(stuff.getBeanContexts()); |
| |
| //Resournce Injection Service |
| CdiResourceInjectionService injectionService = (CdiResourceInjectionService) webBeansContext.getService(ResourceInjectionService.class); |
| injectionService.setAppContext(stuff.getAppContext()); |
| |
| //Deploy the beans |
| try { |
| //Load Extensions |
| loadExtensions(appContext); |
| |
| //Initialize contexts |
| this.contextsService.init(startupObject); |
| |
| //Configure Default Beans |
| // need to be done before fireBeforeBeanDiscoveryEvent |
| deployer.configureDefaultBeans(); |
| |
| //Fire Event |
| deployer.fireBeforeBeanDiscoveryEvent(); |
| |
| //Scanning process |
| logger.debug("Scanning classpaths for beans artifacts."); |
| |
| if (scannerService instanceof CdiScanner) { |
| final CdiScanner service = (CdiScanner) scannerService; |
| service.init(startupObject); |
| } else { |
| new CdiScanner().init(startupObject); |
| } |
| |
| //Scan |
| this.scannerService.scan(); |
| |
| //Deploy bean from XML. Also configures deployments, interceptors, decorators. |
| deployer.deployFromXML(scannerService); |
| |
| //Checking stereotype conditions |
| deployer.checkStereoTypes(scannerService); |
| |
| //Discover classpath classes |
| deployManagedBeans(scannerService.getBeanClasses(), stuff.getBeanContexts()); |
| |
| for (BeanContext beanContext : stuff.getBeanContexts()) { |
| if (!beanContext.isCdiCompatible()) continue; |
| |
| final Class implClass = beanContext.getManagedClass(); |
| |
| //Define annotation type |
| final AnnotatedType<Object> annotatedType = webBeansContext.getAnnotatedElementFactory().newAnnotatedType(implClass); |
| |
| //Fires ProcessAnnotatedType |
| final ProcessAnnotatedTypeImpl<?> processAnnotatedEvent = webBeansContext.getWebBeansUtil().fireProcessAnnotatedTypeEvent(annotatedType); |
| |
| // TODO Can you really veto an EJB? |
| //if veto() is called |
| if (processAnnotatedEvent.isVeto()) { |
| continue; |
| } |
| |
| final CdiEjbBean<Object> bean = new CdiEjbBean<Object>(beanContext, webBeansContext); |
| bean.setAnnotatedType((AnnotatedType<Object>) processAnnotatedEvent.getAnnotatedType()); // update AnnotatedType -- can be updated in extensions |
| |
| beanContext.set(CdiEjbBean.class, bean); |
| beanContext.set(CurrentCreationalContext.class, new CurrentCreationalContext()); |
| beanContext.addSystemInterceptor(new CdiInterceptor(bean, beanManager, cdiPlugin.getContexsServices())); |
| |
| EjbUtility.fireEvents((Class<Object>) implClass, bean, (ProcessAnnotatedTypeImpl<Object>) processAnnotatedEvent); |
| |
| beanContext.initIsPassivationScope(); |
| |
| webBeansContext.getWebBeansUtil().setInjectionTargetBeanEnableFlag(bean); |
| |
| Class clazz = beanContext.getBeanClass(); |
| while (clazz.isAnnotationPresent(Specializes.class)) { |
| clazz = clazz.getSuperclass(); |
| |
| if (clazz == null || Object.class.equals(clazz)) break; |
| |
| final CdiEjbBean<Object> superBean = new CdiEjbBean<Object>(beanContext, webBeansContext, clazz); |
| |
| EjbBeanCreatorImpl<?> ejbBeanCreator = new EjbBeanCreatorImpl(superBean); |
| |
| //Define meta-data |
| ejbBeanCreator.defineSerializable(); |
| ejbBeanCreator.defineStereoTypes(); |
| ejbBeanCreator.defineScopeType("Session Bean implementation class : " + clazz.getName() + " stereotypes must declare same @ScopeType annotations", false); |
| ejbBeanCreator.defineQualifier(); |
| ejbBeanCreator.defineName(WebBeansUtil.getManagedBeanDefaultName(clazz.getSimpleName())); |
| |
| bean.specialize(superBean); |
| |
| EjbUtility.defineSpecializedData(clazz, bean); |
| } |
| } |
| |
| //Check Specialization |
| deployer.checkSpecializations(scannerService); |
| |
| //Fire Event |
| deployer.fireAfterBeanDiscoveryEvent(); |
| |
| //Validate injection Points |
| deployer.validateInjectionPoints(); |
| |
| for (BeanContext beanContext : stuff.getBeanContexts()) { |
| if (!beanContext.isCdiCompatible() || beanContext.isDynamicallyImplemented()) continue; |
| final CdiEjbBean bean = beanContext.get(CdiEjbBean.class); |
| |
| // The interceptor stack is empty until validateInjectionPoints is called as it does more than validate. |
| final List<InterceptorData> datas = bean.getInterceptorStack(); |
| |
| final List<org.apache.openejb.core.interceptor.InterceptorData> converted = new ArrayList<org.apache.openejb.core.interceptor.InterceptorData>(); |
| for (InterceptorData data : datas) { |
| // todo this needs to use the code in InterceptorBindingBuilder that respects override rules and private methods |
| final org.apache.openejb.core.interceptor.InterceptorData openejbData = org.apache.openejb.core.interceptor.InterceptorData.scan(data.getInterceptorClass()); |
| if (data.isDefinedInMethod()) { |
| final Method method = data.getInterceptorBindingMethod(); |
| beanContext.addCdiMethodInterceptor(method, openejbData); |
| } else { |
| converted.add(openejbData); |
| } |
| } |
| |
| beanContext.setCdiInterceptors(converted); |
| } |
| |
| //Fire Event |
| deployer.fireAfterDeploymentValidationEvent(); |
| |
| for (BeanContext beanContext : stuff.getBeanContexts()) { |
| |
| final CdiEjbBean<Object> bean = beanContext.get(CdiEjbBean.class); |
| |
| if (bean == null) continue; |
| |
| final BeanManagerImpl manager = webBeansContext.getBeanManagerImpl(); |
| manager.addBean(new NewCdiEjbBean<Object>(bean)); |
| } |
| |
| } catch (Exception e1) { |
| Assembler.logger.error("CDI Beans module deployment failed", e1); |
| throw new OpenEJBRuntimeException(e1); |
| } |
| //Start actual starting on sub-classes |
| afterStartApplication(startupObject); |
| |
| if (beanManager instanceof WebappBeanManager) { |
| ((WebappBeanManager) beanManager).afterStart(); |
| } |
| } finally { |
| Thread.currentThread().setContextClassLoader(oldCl); |
| |
| // cleanup threadlocal used to enrich cdi context manually |
| OptimizedLoaderService.ADDITIONAL_EXTENSIONS.remove(); |
| CdiScanner.ADDITIONAL_CLASSES.remove(); |
| } |
| |
| logger.info("OpenWebBeans Container has started, it took {0} ms.", Long.toString(System.currentTimeMillis() - begin)); |
| } |
| |
| public static class NewEjbBean<T> extends CdiEjbBean<T> implements NewBean<T> { |
| |
| public NewEjbBean(BeanContext beanContext, WebBeansContext webBeansContext) { |
| super(beanContext, webBeansContext); |
| } |
| |
| |
| } |
| |
| private void loadExtensions(AppContext appContext) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { |
| final ExtensionLoader extensionLoader = webBeansContext.getExtensionLoader(); |
| |
| // Load regularly visible Extensions |
| extensionLoader.loadExtensionServices(Thread.currentThread().getContextClassLoader()); // init in OpenEJBLifecycle |
| |
| |
| // Load any potentially misplaced extensions -- TCK seems to be full of them |
| // This could perhaps be improved or addressed elsewhere |
| // final String s = "WEB-INF/classes/META-INF/services/javax.enterprise.inject.spi.Extension"; |
| // final ArrayList<URL> list = Collections.list(appContext.getClassLoader().getResources(s)); |
| // for (URL url : list) { |
| // final String className = readContents(url).trim(); |
| // |
| // final Class<?> extensionClass = appContext.getClassLoader().loadClass(className); |
| // |
| // if (Extension.class.isAssignableFrom(extensionClass)) { |
| // final Extension extension = (Extension) extensionClass.newInstance(); |
| // extensionLoader.addExtension(extension); |
| // } |
| // } |
| } |
| |
| private void deployManagedBeans(Set<Class<?>> beanClasses, List<BeanContext> ejbs) { |
| Set<Class<?>> managedBeans = new HashSet<Class<?>>(beanClasses); |
| for (BeanContext beanContext: ejbs) { |
| if (beanContext.getComponentType().isSession()) { |
| managedBeans.remove(beanContext.getBeanClass()); |
| } |
| } |
| // Start from the class |
| final Map<Class<?>, AnnotatedType<?>> annotatedTypes = new LinkedHashMap<Class<?>, AnnotatedType<?>>(); |
| for (Class<?> implClass : managedBeans) { // create all annotated types first to be sure extensions can use it during the fire |
| //Define annotation type |
| final AnnotatedType<?> at = webBeansContext.getAnnotatedElementFactory().newAnnotatedType(implClass); |
| if (at != null) { |
| annotatedTypes.put(implClass, at); |
| } else { |
| logger.warning("an error occured create AnnotatedType for class " |
| + implClass.getName() + ". Skipping."); |
| } |
| } |
| for (Map.Entry<Class<?>, AnnotatedType<?>> implClass : annotatedTypes.entrySet()) { |
| //Fires ProcessAnnotatedType |
| final ProcessAnnotatedTypeImpl<?> processAnnotatedEvent; |
| try { |
| processAnnotatedEvent = webBeansContext.getWebBeansUtil().fireProcessAnnotatedTypeEvent(implClass.getValue()); |
| } catch (RuntimeException cnfe) { |
| if (skipClassNotFoundError && rootCauseIsClassNotFound(cnfe)) { |
| logger.error("an error occured firing ProcessAnnotatedEvent for class " |
| + implClass.getValue().getJavaClass().getName() + ". Skipping the bean."); |
| logger.debug("Skipping bean cause", cnfe); |
| continue; |
| } else { |
| throw cnfe; |
| } |
| } |
| |
| //if veto() is called |
| if (processAnnotatedEvent.isVeto()) { |
| continue; |
| } |
| |
| deployer.defineManagedBean((Class<Object>) implClass.getKey(), (ProcessAnnotatedTypeImpl<Object>) processAnnotatedEvent); |
| } |
| annotatedTypes.clear(); |
| } |
| |
| private static boolean rootCauseIsClassNotFound(final RuntimeException re) { |
| Throwable e = re; |
| e.getStackTrace(); |
| while (e != null) { |
| if (e instanceof ClassNotFoundException) { |
| return true; |
| } |
| e = e.getCause(); |
| } |
| return false; |
| } |
| |
| @Override |
| public void stopApplication(Object endObject) |
| { |
| logger.debug("OpenWebBeans Container is stopping."); |
| |
| try |
| { |
| //Sub-classes operations |
| beforeStopApplication(null); |
| |
| //Fire shut down |
| this.beanManager.fireEvent(new BeforeShutdownImpl(), BeansDeployer.EMPTY_ANNOTATION_ARRAY); |
| |
| //Destroys context |
| this.contextsService.destroy(null); |
| |
| //Unbind BeanManager |
| if (jndiService != null) { |
| jndiService.unbind(WebBeansConstants.WEB_BEANS_MANAGER_JNDI_NAME); |
| } |
| |
| //Free all plugin resources |
| ((CdiPlugin) webBeansContext.getPluginLoader().getEjbPlugin()).clearProxies(); |
| webBeansContext.getPluginLoader().shutDown(); |
| |
| //Clear extensions |
| webBeansContext.getExtensionLoader().clear(); |
| |
| //Delete Resolutions Cache |
| beanManager.getInjectionResolver().clearCaches(); |
| |
| //Delte proxies |
| webBeansContext.getProxyFactory().clear(); |
| |
| //Delete AnnotateTypeCache |
| webBeansContext.getAnnotatedElementFactory().clear(); |
| |
| //After Stop |
| afterStopApplication(null); |
| |
| // Clear BeanManager |
| this.beanManager.clear(); |
| |
| // Clear singleton list |
| WebBeansFinder.clearInstances(WebBeansUtil.getCurrentClassLoader()); |
| |
| } |
| catch (Exception e) |
| { |
| logger.error("An error occured while stopping the container.", e); |
| } |
| |
| } |
| |
| /** |
| * @return the scannerService |
| */ |
| protected ScannerService getScannerService() |
| { |
| return scannerService; |
| } |
| |
| /** |
| * @return the contextsService |
| */ |
| public ContextsService getContextService() |
| { |
| return contextsService; |
| } |
| |
| /** |
| * @return the deployer |
| */ |
| protected BeansDeployer getDeployer() |
| { |
| return deployer; |
| } |
| |
| /** |
| * @return the xmlDeployer |
| */ |
| protected WebBeansXMLConfigurator getXmlDeployer() |
| { |
| return xmlDeployer; |
| } |
| |
| /** |
| * @return the jndiService |
| */ |
| protected JNDIService getJndiService() |
| { |
| return jndiService; |
| } |
| |
| @Override |
| public void initApplication(Properties properties) |
| { |
| afterInitApplication(properties); |
| } |
| |
| protected void beforeInitApplication(Properties properties) |
| { |
| //Do nothing as default |
| } |
| |
| protected void afterInitApplication(Properties properties) |
| { |
| //Do nothing as default |
| } |
| |
| protected void afterStartApplication(final Object startupObject) |
| { |
| // no-op |
| } |
| |
| public void startServletContext(final ServletContext servletContext) { |
| service = initializeServletContext(servletContext, webBeansContext); |
| } |
| |
| public static ScheduledExecutorService initializeServletContext(final ServletContext servletContext, WebBeansContext context) { |
| String strDelay = context.getOpenWebBeansConfiguration().getProperty(OpenWebBeansConfiguration.CONVERSATION_PERIODIC_DELAY, "150000"); |
| long delay = Long.parseLong(strDelay); |
| |
| final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1, new ThreadFactory() { |
| @Override |
| public Thread newThread(Runnable runable) { |
| Thread t = new Thread(runable, "OwbConversationCleaner-" + servletContext.getContextPath()); |
| t.setDaemon(true); |
| return t; |
| } |
| }); |
| executorService.scheduleWithFixedDelay(new ConversationCleaner(context), delay, delay, TimeUnit.MILLISECONDS); |
| |
| ELAdaptor elAdaptor = context.getService(ELAdaptor.class); |
| ELResolver resolver = elAdaptor.getOwbELResolver(); |
| //Application is configured as JSP |
| if (context.getOpenWebBeansConfiguration().isJspApplication()) { |
| logger.debug("Application is configured as JSP. Adding EL Resolver."); |
| |
| JspFactory factory = JspFactory.getDefaultFactory(); |
| if (factory != null) { |
| JspApplicationContext applicationCtx = factory.getJspApplicationContext(servletContext); |
| applicationCtx.addELResolver(resolver); |
| } else { |
| logger.debug("Default JspFactory instance was not found"); |
| } |
| } |
| |
| // Add BeanManager to the 'javax.enterprise.inject.spi.BeanManager' servlet context attribute |
| servletContext.setAttribute(BeanManager.class.getName(), context.getBeanManagerImpl()); |
| |
| return executorService; |
| } |
| |
| /** |
| * Conversation cleaner thread, that |
| * clears unused conversations. |
| * |
| */ |
| private static class ConversationCleaner implements Runnable |
| { |
| private final WebBeansContext webBeansContext; |
| |
| private ConversationCleaner(WebBeansContext webBeansContext) { |
| this.webBeansContext = webBeansContext; |
| } |
| |
| public void run() |
| { |
| webBeansContext.getConversationManager().destroyWithRespectToTimout(); |
| |
| } |
| } |
| |
| protected void afterStopApplication(Object stopObject) throws Exception |
| { |
| |
| //Clear the resource injection service |
| ResourceInjectionService injectionServices = webBeansContext.getService(ResourceInjectionService.class); |
| if(injectionServices != null) |
| { |
| injectionServices.clear(); |
| } |
| |
| //Comment out for commit OWB-502 |
| //ContextFactory.cleanUpContextFactory(); |
| |
| this.cleanupShutdownThreadLocals(); |
| |
| WebBeansFinder.clearInstances(WebBeansUtil.getCurrentClassLoader()); |
| } |
| |
| /** |
| * Ensures that all ThreadLocals, which could have been set in this |
| * (shutdown-) Thread, are removed in order to prevent memory leaks. |
| */ |
| private void cleanupShutdownThreadLocals() |
| { |
| // TODO maybe there are more to cleanup |
| InjectionPointBean.removeThreadLocal(); |
| } |
| |
| /** |
| * Returns servelt context otherwise throws exception. |
| * @param object object |
| * @return servlet context |
| */ |
| private Object getServletContext(Object object) { |
| if (object instanceof ServletContextEvent) { |
| object = ((ServletContextEvent) object).getServletContext(); |
| return object; |
| } |
| return object; |
| } |
| |
| protected void beforeStartApplication(Object startupObject) |
| { |
| //Do nothing as default |
| } |
| |
| protected void beforeStopApplication(Object stopObject) throws Exception |
| { |
| if(service != null) |
| { |
| service.shutdownNow(); |
| } |
| } |
| } |