/****************************************************************************** * Copyright (c) 2006, 2010 VMware Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0 * is available at http://www.opensource.org/licenses/apache2.0.php. * You may elect to redistribute this code under either of these licenses. * * Contributors: * VMware Inc. *****************************************************************************/ package org.eclipse.gemini.blueprint.context.support; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.gemini.blueprint.context.DelegatedExecutionOsgiBundleApplicationContext; import org.eclipse.gemini.blueprint.context.DependencyAwareBeanFactoryPostProcessor; import org.eclipse.gemini.blueprint.context.DependencyInitializationAwareBeanPostProcessor; import org.eclipse.gemini.blueprint.context.OsgiBundleApplicationContextExecutor; import org.eclipse.gemini.blueprint.context.event.OsgiBundleApplicationContextEventMulticaster; import org.eclipse.gemini.blueprint.context.event.OsgiBundleApplicationContextEventMulticasterAdapter; import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextClosedEvent; import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextFailedEvent; import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextRefreshedEvent; import org.eclipse.gemini.blueprint.util.OsgiBundleUtils; import org.eclipse.gemini.blueprint.util.OsgiStringUtils; import org.eclipse.gemini.blueprint.util.internal.PrivilegedUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextException; import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.OrderComparator; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** * OSGi-specific application context that delegates the execution of its life cycle methods to a different class. The * main reason behind this is to <em>break</em> the startup of the application context in steps that can be executed * asynchronously. <p/> <p/> <p/> The {@link #refresh()} and {@link #close()} methods delegate their execution to an * {@link OsgiBundleApplicationContextExecutor} class that chooses how to call the lifecycle methods. <p/> <p/> <p/> One * can still call the 'traditional' lifecycle methods through {@link #normalRefresh()} and {@link #normalClose()}. * * @author Costin Leau * @see DelegatedExecutionOsgiBundleApplicationContext */ public abstract class AbstractDelegatedExecutionApplicationContext extends AbstractOsgiBundleApplicationContext implements DelegatedExecutionOsgiBundleApplicationContext { /** * Executor that offers the traditional way of <code>refreshing</code>/ <code>closing</code> of an * ApplicationContext (no conditions have to be met and the refresh happens in only one step). * * @author Costin Leau */ private static class NoDependenciesWaitRefreshExecutor implements OsgiBundleApplicationContextExecutor { private final DelegatedExecutionOsgiBundleApplicationContext context; private NoDependenciesWaitRefreshExecutor(DelegatedExecutionOsgiBundleApplicationContext ctx) { context = ctx; } public void refresh() throws BeansException, IllegalStateException { context.normalRefresh(); } public void close() { context.normalClose(); } } /** * BeanPostProcessor that logs an info message when a bean is created during BeanPostProcessor instantiation, i.e. * when a bean is not eligible for getting processed by all BeanPostProcessors. */ private class BeanPostProcessorChecker implements BeanPostProcessor { private final ConfigurableListableBeanFactory beanFactory; private final int beanPostProcessorTargetCount; public BeanPostProcessorChecker(ConfigurableListableBeanFactory beanFactory, int beanPostProcessorTargetCount) { this.beanFactory = beanFactory; this.beanPostProcessorTargetCount = beanPostProcessorTargetCount; } public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) { if (!(bean instanceof BeanPostProcessor) && this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) { if (logger.isInfoEnabled()) { logger.info("Bean '" + beanName + "' is not eligible for getting processed by all " + "BeanPostProcessors (for example: not eligible for auto-proxying)"); } } return bean; } } /** * Default executor */ private OsgiBundleApplicationContextExecutor executor = new NoDependenciesWaitRefreshExecutor(this); /** * monitor used during refresh/close */ private final Object startupShutdownMonitor = new Object(); /** * Delegated multicaster */ private OsgiBundleApplicationContextEventMulticaster delegatedMulticaster; private ContextClassLoaderProvider cclProvider; /** * Constructs a new <code>AbstractDelegatedExecutionApplicationContext</code> instance. */ public AbstractDelegatedExecutionApplicationContext() { super(); } /** * Constructs a new <code>AbstractDelegatedExecutionApplicationContext</code> instance. * * @param parent parent application context */ public AbstractDelegatedExecutionApplicationContext(ApplicationContext parent) { super(parent); } /** * Delegate execution of refresh method to a third party. This allows breaking the refresh process into several * small pieces providing continuation-like behaviour or completion of the refresh method on several threads, in a * asynch manner. <p/> By default, the refresh method in executed in <em>one go</em> (normal behaviour). <p/> * {@inheritDoc} */ public void refresh() throws BeansException, IllegalStateException { executor.refresh(); } public void normalRefresh() { Assert.notNull(getBundleContext(), "bundle context should be set before refreshing the application context"); try { PrivilegedUtils.executeWithCustomTCCL(contextClassLoaderProvider().getContextClassLoader(), new PrivilegedUtils.UnprivilegedExecution() { public Object run() { AbstractDelegatedExecutionApplicationContext.super.refresh(); sendRefreshedEvent(); return null; } }); } catch (Throwable th) { if (logger.isDebugEnabled()) { logger.debug("Refresh error", th); } sendFailedEvent(th); // propagate exception to the caller // rethrow the problem w/o rewrapping if (th instanceof RuntimeException) { throw (RuntimeException) th; } else { throw (Error) th; } } } public void normalClose() { try { PrivilegedUtils.executeWithCustomTCCL(contextClassLoaderProvider().getContextClassLoader(), new PrivilegedUtils.UnprivilegedExecution() { public Object run() { AbstractDelegatedExecutionApplicationContext.super.doClose(); sendClosedEvent(); return null; } }); } catch (Throwable th) { // send failure event sendClosedEvent(th); // rethrow the problem w/o rewrapping if (th instanceof RuntimeException) { throw (RuntimeException) th; } else { throw (Error) th; } } } // Adds behaviour for isAvailable flag. protected void doClose() { executor.close(); } public void startRefresh() { try { PrivilegedUtils.executeWithCustomTCCL(contextClassLoaderProvider().getContextClassLoader(), new PrivilegedUtils.UnprivilegedExecution<Object>() { public Object run() { synchronized (startupShutdownMonitor) { if (ObjectUtils.isEmpty(getConfigLocations())) { setConfigLocations(getDefaultConfigLocations()); } if (!OsgiBundleUtils.isBundleActive(getBundle()) && !OsgiBundleUtils.isBundleLazyActivated(getBundle())) { throw new ApplicationContextException( "Unable to refresh application context: bundle is neither active nor lazy-activated but " + OsgiStringUtils.bundleStateAsString(getBundle())); } ConfigurableListableBeanFactory beanFactory = null; // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean // factory. beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in // context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans // in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean // creation. registerBeanPostProcessors(beanFactory, DependencyInitializationAwareBeanPostProcessor.class, null, false); return null; } catch (BeansException ex) { // Destroy already created singletons to avoid // dangling resources. beanFactory.destroySingletons(); cancelRefresh(ex); // propagate exception to the caller throw ex; } } } }); } catch (Throwable th) { if (logger.isDebugEnabled()) { logger.debug("Pre refresh error", th); } // send failure event sendFailedEvent(th); // rethrow the problem w/o rewrapping if (th instanceof RuntimeException) { throw (RuntimeException) th; } else { throw (Error) th; } } } public void completeRefresh() { try { PrivilegedUtils.executeWithCustomTCCL(contextClassLoaderProvider().getContextClassLoader(), new PrivilegedUtils.UnprivilegedExecution<Object>() { public Object run() { synchronized (startupShutdownMonitor) { try { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); // Invoke factory processors registered as beans // in the context. invokeBeanFactoryPostProcessors(beanFactory, DependencyAwareBeanFactoryPostProcessor.class, null); // Register bean processors that intercept bean // creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this // context. initApplicationEventMulticaster(); // Initialize other special beans in specific // context // subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) // singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); // everything went okay, post notification sendRefreshedEvent(); return null; } catch (BeansException ex) { // Destroy already created singletons to avoid // dangling // resources. getBeanFactory().destroySingletons(); cancelRefresh(ex); // propagate exception to the caller throw ex; } } } }); } catch (Throwable th) { if (logger.isDebugEnabled()) { logger.debug("Post refresh error", th); } // post notification sendFailedEvent(th); // rethrow the problem w/o rewrapping if (th instanceof RuntimeException) { throw (RuntimeException) th; } else { throw (Error) th; } } } // customized to handle DependencyAwareBeanFactoryPostProcessor classes protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { invokeBeanFactoryPostProcessors(beanFactory, BeanFactoryPostProcessor.class, DependencyAwareBeanFactoryPostProcessor.class); } /** * Instantiate and invoke all registered BeanFactoryPostProcessor beans, respecting explicit order if given. <p/> * Must be called before singleton instantiation. Very similar to * {@link AbstractApplicationContext#invokeBeanFactoryPostProcessors} but allowing exclusion of a certain type. * * @param beanFactory * @param type * @param exclude */ private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, Class<?> type, Class<?> exclude) { // Invoke factory processors registered with the context instance. for (Iterator<BeanFactoryPostProcessor> it = getBeanFactoryPostProcessors().iterator(); it.hasNext();) { BeanFactoryPostProcessor factoryProcessor = it.next(); // check the exclude type if (type.isInstance(factoryProcessor) && (exclude == null || !exclude.isInstance(factoryProcessor))) { factoryProcessor.postProcessBeanFactory(beanFactory); } } // Do not initialize FactoryBeans here: We need to leave all regular // beans uninitialized to let the bean factory post-processors apply to // them! String[] postProcessorNames = beanFactory.getBeanNamesForType(type, true, false); // Separate between BeanFactoryPostProcessors that implement // PriorityOrdered, // Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); List<String> orderedPostProcessorNames = new ArrayList<String>(); List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); for (int i = 0; i < postProcessorNames.length; i++) { // first check the excluded type if (exclude == null || !isTypeMatch(postProcessorNames[i], exclude)) { if (isTypeMatch(postProcessorNames[i], PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(postProcessorNames[i], BeanFactoryPostProcessor.class)); } else if (isTypeMatch(postProcessorNames[i], Ordered.class)) { orderedPostProcessorNames.add(postProcessorNames[i]); } else { nonOrderedPostProcessorNames.add(postProcessorNames[i]); } } } // First, invoke the BeanFactoryPostProcessors that implement // PriorityOrdered. Collections.sort(priorityOrderedPostProcessors, new OrderComparator()); invokeBeanFactoryPostProcessors(beanFactory, priorityOrderedPostProcessors); // Next, invoke the BeanFactoryPostProcessors that implement Ordered. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); for (Iterator<String> it = orderedPostProcessorNames.iterator(); it.hasNext();) { String postProcessorName = it.next(); orderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class)); } Collections.sort(orderedPostProcessors, new OrderComparator()); invokeBeanFactoryPostProcessors(beanFactory, orderedPostProcessors); // Finally, invoke all other BeanFactoryPostProcessors. List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); for (Iterator<String> it = nonOrderedPostProcessorNames.iterator(); it.hasNext();) { String postProcessorName = it.next(); nonOrderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(beanFactory, nonOrderedPostProcessors); } /** * Invoke given post processors. * * @param beanFactory * @param postProcessors */ private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> postProcessors) { for (BeanFactoryPostProcessor beanFactoryPostProcessor : postProcessors) { beanFactoryPostProcessor.postProcessBeanFactory(beanFactory); } } // customized to handle DependencyInitializationAwareBeanPostProcessor // classes protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) { registerBeanPostProcessors(beanFactory, BeanPostProcessor.class, DependencyInitializationAwareBeanPostProcessor.class, true); } /** * Instantiate and invoke all registered BeanPostProcessor beans, respecting explicit order if given. <p/> Must be * called before any instantiation of application beans. Very similar to * {@link AbstractApplicationContext#invokeBeanFactoryPostProcessors} but allowing exclusion of a certain type. */ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, Class<?> type, Class<?> exclude, boolean check) { String[] postProcessorNames = beanFactory.getBeanNamesForType(type, true, false); if (check) { // Register BeanPostProcessorChecker that logs an info message when // a bean is created during BeanPostProcessor instantiation, i.e. // when // a bean is not eligible for getting processed by all // BeanPostProcessors. int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length; beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); } // Separate between BeanPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>(); List<String> orderedPostProcessorNames = new ArrayList<String>(); List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); for (int i = 0; i < postProcessorNames.length; i++) { // check exclude type first if (exclude == null || !isTypeMatch(postProcessorNames[i], exclude)) { if (isTypeMatch(postProcessorNames[i], PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(postProcessorNames[i], BeanPostProcessor.class)); } else if (isTypeMatch(postProcessorNames[i], Ordered.class)) { orderedPostProcessorNames.add(postProcessorNames[i]); } else { nonOrderedPostProcessorNames.add(postProcessorNames[i]); } } } // First, register the BeanPostProcessors that implement // PriorityOrdered. Collections.sort(priorityOrderedPostProcessors, new OrderComparator()); registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); // Next, register the BeanPostProcessors that implement Ordered. List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>(); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(getBean(postProcessorName, BeanPostProcessor.class)); } Collections.sort(orderedPostProcessors, new OrderComparator()); registerBeanPostProcessors(beanFactory, orderedPostProcessors); // Finally, register all other BeanPostProcessors. List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>(); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(getBean(postProcessorName, BeanPostProcessor.class)); } registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); } /** * Register the given BeanPostProcessor beans. */ private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors) { for (BeanPostProcessor postProcessor : postProcessors) { beanFactory.addBeanPostProcessor(postProcessor); } } public void setExecutor(OsgiBundleApplicationContextExecutor executor) { this.executor = executor; } protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException, BeansException { } public void setDelegatedEventMulticaster(OsgiBundleApplicationContextEventMulticaster multicaster) { this.delegatedMulticaster = multicaster; } /** * Sets the OSGi multicaster by using a Spring {@link ApplicationEventMulticaster}. This method is added as a * covenience. * * @param multicaster Spring multi-caster used for propagating OSGi specific events * @see OsgiBundleApplicationContextEventMulticasterAdapter */ public void setDelegatedEventMulticaster(ApplicationEventMulticaster multicaster) { this.delegatedMulticaster = new OsgiBundleApplicationContextEventMulticasterAdapter(multicaster); } public OsgiBundleApplicationContextEventMulticaster getDelegatedEventMulticaster() { return this.delegatedMulticaster; } private void sendFailedEvent(Throwable cause) { if (delegatedMulticaster != null) delegatedMulticaster.multicastEvent(new OsgiBundleContextFailedEvent(this, this.getBundle(), cause)); } private void sendRefreshedEvent() { if (delegatedMulticaster != null) delegatedMulticaster.multicastEvent(new OsgiBundleContextRefreshedEvent(this, this.getBundle())); } private void sendClosedEvent() { if (delegatedMulticaster != null) delegatedMulticaster.multicastEvent(new OsgiBundleContextClosedEvent(this, this.getBundle())); } private void sendClosedEvent(Throwable cause) { if (delegatedMulticaster != null) delegatedMulticaster.multicastEvent(new OsgiBundleContextClosedEvent(this, this.getBundle(), cause)); } /** * private method used for doing lazy-init-if-not-set for cclProvider */ private ContextClassLoaderProvider contextClassLoaderProvider() { if (cclProvider == null) { DefaultContextClassLoaderProvider defaultProvider = new DefaultContextClassLoaderProvider(); defaultProvider.setBeanClassLoader(getClassLoader()); cclProvider = defaultProvider; } return cclProvider; } /** * Sets the {@link ContextClassLoaderProvider} used by this OSGi application context instance. By default, * {@link DefaultContextClassLoaderProvider} is used. * * @param contextClassLoaderProvider context class loader provider to use * @see ContextClassLoaderProvider * @see DefaultContextClassLoaderProvider */ public void setContextClassLoaderProvider(ContextClassLoaderProvider contextClassLoaderProvider) { this.cclProvider = contextClassLoaderProvider; } }