Java tutorial
/**************************************************************** * 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.james.container.spring.lifecycle.osgi; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.FatalBeanException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.osgi.context.BundleContextAware; import org.springframework.osgi.service.importer.support.Cardinality; import org.springframework.osgi.service.importer.support.OsgiServiceCollectionProxyFactoryBean; import org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean; import org.springframework.util.ReflectionUtils; /** * Abstract base class for {@link BeanPostProcessor} implementations which need to wire stuff via annotations and need to be functional via OSGI. * * Many of this code is borrowed from the spring-dm's class <code>org.springframework.osgi.extensions.annotation.ServiceReferenceInjectionBeanPostProcessor.</code> * * * * * @param <A> */ public abstract class AbstractOSGIAnnotationBeanPostProcessor<A extends Annotation> extends InstantiationAwareBeanPostProcessorAdapter implements BundleContextAware, BeanClassLoaderAware, BeanFactoryAware { public final static long DEFAULT_TIMEOUT = 60 * 1000 * 5; private BundleContext bundleContext; private static Logger logger = LoggerFactory.getLogger(AbstractOSGIAnnotationBeanPostProcessor.class); protected BeanFactory beanFactory; private ClassLoader classLoader; private boolean lookupBeanFactory = true; private long timeout = DEFAULT_TIMEOUT; /** * Set the timeout in milliseconds. The default is 5 minutes * * @param timeout */ public void setTimeout(long timeout) { this.timeout = timeout; } public void setLookupBeanFactory(boolean lookupBeanFactory) { this.lookupBeanFactory = lookupBeanFactory; } private abstract static class ImporterCallAdapter { @SuppressWarnings("rawtypes") static void setInterfaces(Object importer, Class[] classes) { if (importer instanceof OsgiServiceProxyFactoryBean) ((OsgiServiceProxyFactoryBean) importer).setInterfaces(classes); else ((OsgiServiceCollectionProxyFactoryBean) importer).setInterfaces(classes); } static void setBundleContext(Object importer, BundleContext context) { ((BundleContextAware) importer).setBundleContext(context); } static void setBeanClassLoader(Object importer, ClassLoader cl) { ((BeanClassLoaderAware) importer).setBeanClassLoader(cl); } static void setCardinality(Object importer, Cardinality cardinality) { if (importer instanceof OsgiServiceProxyFactoryBean) ((OsgiServiceProxyFactoryBean) importer).setCardinality(cardinality); else ((OsgiServiceCollectionProxyFactoryBean) importer).setCardinality(cardinality); } static void afterPropertiesSet(Object importer) throws Exception { ((InitializingBean) importer).afterPropertiesSet(); } static void setFilter(Object importer, String filter) throws Exception { if (importer instanceof OsgiServiceProxyFactoryBean) ((OsgiServiceProxyFactoryBean) importer).setFilter(filter); else ((OsgiServiceCollectionProxyFactoryBean) importer).setFilter(filter); } @SuppressWarnings("unused") static void setServiceBean(Object importer, String name) { if (importer instanceof OsgiServiceProxyFactoryBean) ((OsgiServiceProxyFactoryBean) importer).setServiceBeanName(name); else ((OsgiServiceCollectionProxyFactoryBean) importer).setServiceBeanName(name); } } /** * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader) */ public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } /** * process FactoryBean created objects, since these will not have had * services injected. * * @param bean * @param beanName */ @Override public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException { if (logger.isDebugEnabled()) logger.debug("processing [" + bean.getClass().getName() + ", " + beanName + "]"); // Catch FactoryBean created instances. if (!(bean instanceof FactoryBean) && beanFactory.containsBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName)) { injectServices(bean, beanName); } return bean; } /* private version of the injector can use */ private void injectServices(final Object bean, final String beanName) { ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() { public void doWith(Method method) { A s = AnnotationUtils.getAnnotation(method, getAnnotation()); if (s != null && method.getParameterTypes().length == 1) { try { if (logger.isDebugEnabled()) logger.debug("Processing annotation [" + s + "] for [" + bean.getClass().getName() + "." + method.getName() + "()] on bean [" + beanName + "]"); method.invoke(bean, getServiceImporter(s, method, beanName).getObject()); } catch (Exception e) { throw new IllegalArgumentException("Error processing annotation " + s, e); } } } }); } @Override @SuppressWarnings("rawtypes") public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { MutablePropertyValues newprops = new MutablePropertyValues(pvs); for (PropertyDescriptor pd : pds) { A s = hasAnnotatedProperty(pd); if (s != null && !pvs.contains(pd.getName())) { try { if (logger.isDebugEnabled()) logger.debug( "Processing annotation [" + s + "] for [" + beanName + "." + pd.getName() + "]"); FactoryBean importer = getServiceImporter(s, pd.getWriteMethod(), beanName); // BPPs are created in stageOne(), even though they are run in stageTwo(). This check means that // the call to getObject() will not fail with ServiceUnavailable. This is safe to do because // ServiceReferenceDependencyBeanFactoryPostProcessor will ensure that mandatory services are // satisfied before stageTwo() is run. if (bean instanceof BeanPostProcessor) { ImporterCallAdapter.setCardinality(importer, Cardinality.C_0__1); } newprops.addPropertyValue(pd.getName(), importer.getObject()); } catch (Exception e) { throw new FatalBeanException("Could not create service reference", e); } } } return newprops; } @SuppressWarnings("rawtypes") private FactoryBean getServiceImporter(final A s, final Method writeMethod, String beanName) throws Exception { // Invocations will block here, so although the ApplicationContext is // created nothing will // proceed until all the dependencies are satisfied. Class<?>[] params = writeMethod.getParameterTypes(); if (params.length != 1) { throw new IllegalArgumentException("Setter for [" + beanName + "] must have only one argument"); } if (lookupBeanFactory) { if (logger.isDebugEnabled()) logger.debug("Lookup the bean via the BeanFactory"); final Class<?> clazz = writeMethod.getParameterTypes()[0]; Object bean; try { bean = getBeanFromFactory(s, clazz); } catch (NoSuchBeanDefinitionException e) { // We was not able to find the bean in the factory so fallback to the osgi registry bean = null; } if (bean != null) { final Object fBean = bean; // Create a new FactoryBean which just return the found beab return new FactoryBean() { @Override public Object getObject() throws Exception { return fBean; } @Override public Class getObjectType() { return fBean.getClass(); } @Override public boolean isSingleton() { return true; } }; } } // The bean was not found in the BeanFactory. Its time to lookup it via the OSGI-Registry return getResourceProperty(new OsgiServiceProxyFactoryBean(), getFilter(s), writeMethod, beanName); } @SuppressWarnings("rawtypes") private FactoryBean getResourceProperty(OsgiServiceProxyFactoryBean pfb, String filter, Method writeMethod, String beanName) throws Exception { pfb.setTimeout(timeout); // check if the we have a name for the requested bean. If so we set the filter for it if (filter != null) { ImporterCallAdapter.setFilter(pfb, filter); } ImporterCallAdapter.setInterfaces(pfb, writeMethod.getParameterTypes()); ImporterCallAdapter.setBundleContext(pfb, bundleContext); ImporterCallAdapter.setBeanClassLoader(pfb, classLoader); ImporterCallAdapter.afterPropertiesSet(pfb); return pfb; } private A hasAnnotatedProperty(PropertyDescriptor propertyDescriptor) { Method setter = propertyDescriptor.getWriteMethod(); return setter != null ? AnnotationUtils.getAnnotation(setter, getAnnotation()) : null; } /** * @see org.springframework.osgi.context.BundleContextAware#setBundleContext(org.osgi.framework.BundleContext) */ public void setBundleContext(BundleContext context) { this.bundleContext = context; } /** * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) */ public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } /** * Return the class of the {@link Annotation} * * @return clazz */ protected abstract Class<A> getAnnotation(); /** * Return the filter which should get used to lookup the service in the osgi registry. * If no special filter should be used, just return null * * @param annotation * @return filter */ protected abstract String getFilter(A annotation); /** * Return the Bean lookup-ed from the {@link BeanFactory}. If non can be found just return null * * @param a * @param clazz * @return bean */ protected abstract Object getBeanFromFactory(A a, Class<?> clazz); }