Java tutorial
/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.config.spring; import static org.apache.commons.lang.StringUtils.EMPTY; import static org.mule.config.i18n.MessageFactory.createStaticMessage; import org.mule.api.Injector; import org.mule.api.MuleContext; import org.mule.api.MuleException; import org.mule.api.MuleRuntimeException; import org.mule.api.lifecycle.InitialisationException; import org.mule.api.lifecycle.LifecycleException; import org.mule.api.registry.LifecycleRegistry; import org.mule.api.registry.RegistrationException; import org.mule.api.registry.TransformerResolver; import org.mule.api.transformer.Converter; import org.mule.config.spring.processors.PostRegistrationActionsPostProcessor; import org.mule.lifecycle.RegistryLifecycleManager; import org.mule.lifecycle.phases.NotInLifecyclePhase; import org.mule.registry.AbstractRegistry; import org.mule.util.StringUtils; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.springframework.beans.FatalBeanException; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; public class SpringRegistry extends AbstractRegistry implements LifecycleRegistry, Injector { public static final String REGISTRY_ID = "org.mule.Registry.Spring"; /** * Key used to lookup Spring Application Context from SpringRegistry via Mule's * Registry interface. */ public static final String SPRING_APPLICATION_CONTEXT = "springApplicationContext"; protected ApplicationContext applicationContext; private boolean readOnly; private RegistrationDelegate registrationDelegate; //This is used to track the Spring context lifecycle since there is no way to confirm the //lifecycle phase from the application context protected AtomicBoolean springContextInitialised = new AtomicBoolean(false); public SpringRegistry(ApplicationContext applicationContext, MuleContext muleContext) { super(REGISTRY_ID, muleContext); setApplicationContext(applicationContext); } public SpringRegistry(String id, ApplicationContext applicationContext, MuleContext muleContext) { super(id, muleContext); setApplicationContext(applicationContext); } public SpringRegistry(ConfigurableApplicationContext applicationContext, ApplicationContext parentContext, MuleContext muleContext) { super(REGISTRY_ID, muleContext); applicationContext.setParent(parentContext); setApplicationContext(applicationContext); } public SpringRegistry(String id, ConfigurableApplicationContext applicationContext, ApplicationContext parentContext, MuleContext muleContext) { super(id, muleContext); applicationContext.setParent(parentContext); setApplicationContext(applicationContext); } private void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; if (applicationContext instanceof ConfigurableApplicationContext) { readOnly = false; registrationDelegate = new ConfigurableRegistrationDelegate( (ConfigurableApplicationContext) applicationContext); } else { readOnly = true; registrationDelegate = new ReadOnlyRegistrationDelegate(); } } @Override protected void doInitialise() throws InitialisationException { if (!readOnly) { ((ConfigurableApplicationContext) applicationContext).refresh(); } initTransformers(); //This is used to track the Spring context lifecycle since there is no way to confirm the lifecycle phase from the application context springContextInitialised.set(true); } /** * Forces prototype instances of {@link TransformerResolver} * and {@link Converter} to be created, so that * {@link PostRegistrationActionsPostProcessor} can work * its magic */ private void initTransformers() { applicationContext.getBeansOfType(TransformerResolver.class); applicationContext.getBeansOfType(Converter.class); } @Override public void doDispose() { // check we aren't trying to close a context which has never been started, // spring's appContext.isActive() isn't working for this case if (!springContextInitialised.get()) { return; } if (!isReadOnly() && ((ConfigurableApplicationContext) applicationContext).isActive()) { ((ConfigurableApplicationContext) applicationContext).close(); } // release the circular implicit ref to MuleContext applicationContext = null; this.springContextInitialised.set(false); } @Override protected RegistryLifecycleManager createLifecycleManager() { return new SpringRegistryLifecycleManager(getRegistryId(), this, muleContext); } /** * {@inheritDoc} * @return the result of invoking {@link #lookupObject(String, boolean)} with {@code true} as the second argument */ @Override public <T> T lookupObject(String key) { return (T) lookupObject(key, true); } /** * If looks for the bean registered under {@code key}. * If the returned bean is a prototype and {@code applyLifecycle} * is {@code true}, then the completed lifecycle phases * are applied to the returning bean * * @param key the key of the object you're looking for * @param applyLifecycle if lifecycle should be applied to the returned object. * Passing {@code true} doesn't guarantee that the lifecycle is applied * @return object or {@code null} if not found */ @SuppressWarnings("unchecked") @Override public Object lookupObject(String key, boolean applyLifecycle) { if (StringUtils.isBlank(key)) { logger.warn(createStaticMessage("Detected a lookup attempt with an empty or null key").getMessage(), new Throwable().fillInStackTrace()); return null; } if (key.equals(SPRING_APPLICATION_CONTEXT) && applicationContext != null) { return applicationContext; } else { Object object; try { object = applicationContext.getBean(key); } catch (NoSuchBeanDefinitionException e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); } return null; } if (applyLifecycle && !applicationContext.isSingleton(key)) { try { getLifecycleManager().applyCompletedPhases(object); } catch (Exception e) { throw new MuleRuntimeException( createStaticMessage("Could not apply lifecycle into prototype object " + key), e); } } return object; } } @Override public <T> Collection<T> lookupObjects(Class<T> type) { return lookupByType(type).values(); } @Override public <T> Collection<T> lookupLocalObjects(Class<T> type) { return internalLookupByTypeWithoutAncestors(type, true, true).values(); } /** * For lifecycle we only want spring to return singleton objects from it's application context */ @Override public <T> Collection<T> lookupObjectsForLifecycle(Class<T> type) { return lookupEntriesForLifecycle(type).values(); } @Override public <T> Map<String, T> lookupByType(Class<T> type) { return internalLookupByType(type, true, true); } @Override public void registerObject(String key, Object value) throws RegistrationException { registrationDelegate.registerObject(key, value); } @Override public void registerObject(String key, Object value, Object metadata) throws RegistrationException { registrationDelegate.registerObject(key, value, metadata); } @Override public void registerObjects(Map<String, Object> objects) throws RegistrationException { registrationDelegate.registerObjects(objects); } @Override protected Object doUnregisterObject(String key) throws RegistrationException { return registrationDelegate.unregisterObject(key); } /** * Will fire any lifecycle methods according to the current lifecycle without actually * registering the object in the registry. This is useful for prototype objects that are created per request and would * clutter the registry with single use objects. * * @param object the object to process * @return the same object with lifecycle methods called (if it has any) * @throws org.mule.api.MuleException if the registry fails to perform the lifecycle change for the object. */ @Override public Object applyLifecycle(Object object) throws MuleException { getLifecycleManager().applyCompletedPhases(object); return object; } @Override public Object applyLifecycle(Object object, String phase) throws MuleException { if (phase == null) { getLifecycleManager().applyCompletedPhases(object); } else { getLifecycleManager().applyPhase(object, NotInLifecyclePhase.PHASE_NAME, phase); } return object; } @Override public <T> T inject(T object) { try { return initialiseObject((ConfigurableApplicationContext) applicationContext, EMPTY, object); } catch (LifecycleException e) { throw new MuleRuntimeException(e); } } private <T> T initialiseObject(ConfigurableApplicationContext applicationContext, String key, T object) throws LifecycleException { applicationContext.getBeanFactory().autowireBean(object); T initialised = (T) applicationContext.getBeanFactory().initializeBean(object, key); return initialised; } protected <T> Map<String, T> internalLookupByType(Class<T> type, boolean nonSingletons, boolean eagerInit) { try { return BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, type, nonSingletons, eagerInit); } catch (FatalBeanException fbex) { // FBE is a result of a broken config, propagate it (see MULE-3297 for more details) String message = String.format("Failed to lookup beans of type %s from the Spring registry", type); throw new MuleRuntimeException(createStaticMessage(message), fbex); } catch (Exception e) { logger.debug(e.getMessage(), e); return Collections.emptyMap(); } } protected <T> Map<String, T> internalLookupByTypeWithoutAncestors(Class<T> type, boolean nonSingletons, boolean eagerInit) { try { return applicationContext.getBeansOfType(type, nonSingletons, eagerInit); } catch (FatalBeanException fbex) { // FBE is a result of a broken config, propagate it (see MULE-3297 for more details) String message = String.format("Failed to lookup beans of type %s from the Spring registry", type); throw new MuleRuntimeException(createStaticMessage(message), fbex); } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); } return Collections.emptyMap(); } } protected <T> Map<String, T> lookupEntriesForLifecycle(Class<T> type) { return internalLookupByTypeWithoutAncestors(type, false, false); } protected Map<String, Object> getDependencies(String key) { if (!readOnly) { Map<String, Object> dependents = new HashMap<>(); for (String dependentKey : ((ConfigurableApplicationContext) applicationContext).getBeanFactory() .getDependenciesForBean(key)) { boolean isBeanDefinition = ((ConfigurableApplicationContext) applicationContext).getBeanFactory() .containsBeanDefinition(dependentKey); if (isBeanDefinition && applicationContext.isSingleton(dependentKey)) { dependents.put(dependentKey, get(dependentKey)); } } return dependents; } throw new UnsupportedOperationException( "This operation is only available when this registry is backed by a ConfigurableApplicationContext"); } private class ConfigurableRegistrationDelegate implements RegistrationDelegate { private final ConfigurableApplicationContext applicationContext; private ConfigurableRegistrationDelegate(ConfigurableApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override public void registerObject(String key, Object value) throws RegistrationException { doRegisterObject(key, value); } @Override public void registerObject(String key, Object value, Object metadata) throws RegistrationException { registerObject(key, value); } @Override public void registerObjects(Map<String, Object> objects) throws RegistrationException { if (objects == null || objects.isEmpty()) { return; } for (Map.Entry<String, Object> entry : objects.entrySet()) { registerObject(entry.getKey(), entry.getValue()); } } @Override public Object unregisterObject(String key) throws RegistrationException { Object object = applicationContext.getBean(key); if (applicationContext.getBeanFactory().containsBeanDefinition(key)) { ((BeanDefinitionRegistry) applicationContext.getBeanFactory()).removeBeanDefinition(key); } ((DefaultListableBeanFactory) applicationContext.getBeanFactory()).destroySingleton(key); return object; } private synchronized void doRegisterObject(String key, Object value) throws RegistrationException { if (applicationContext.containsBean(key)) { if (logger.isWarnEnabled()) { logger.warn(String.format( "Spring registry already contains an object named '%s'. The previous object will be overwritten.", key)); } SpringRegistry.this.unregisterObject(key); } try { value = initialiseObject(applicationContext, key, value); applyLifecycle(value); applicationContext.getBeanFactory().registerSingleton(key, value); } catch (Exception e) { throw new RegistrationException(createStaticMessage("Could not register object for key " + key), e); } } } //////////////////////////////////////////////////////////////////////////////////// // Registry meta-data //////////////////////////////////////////////////////////////////////////////////// @Override public boolean isReadOnly() { return readOnly; } @Override public boolean isRemote() { return false; } }