001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.xbean.server.spring.configuration;
018    
019    import java.util.Collections;
020    import java.util.HashMap;
021    import java.util.Iterator;
022    import java.util.Map;
023    
024    import org.apache.xbean.kernel.Kernel;
025    import org.apache.xbean.kernel.ServiceAlreadyExistsException;
026    import org.apache.xbean.kernel.ServiceName;
027    import org.apache.xbean.kernel.ServiceNotFoundException;
028    import org.apache.xbean.kernel.ServiceRegistrationException;
029    import org.apache.xbean.kernel.StaticServiceFactory;
030    import org.apache.xbean.kernel.StringServiceName;
031    import org.apache.xbean.server.spring.loader.SpringLoader;
032    import org.springframework.beans.factory.config.BeanDefinition;
033    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
034    import org.springframework.context.support.AbstractXmlApplicationContext;
035    
036    /**
037     * SpringConfiguration that registers and unregisters services that have been defined in a Spring ApplicationContext.
038     *
039     * @author Dain Sundstrom
040     * @version $Id$
041     * @since 2.0
042     */
043    public class SpringConfiguration {
044        private final AbstractXmlApplicationContext applicationContext;
045        private final Map serviceFactories;
046        private final Kernel kernel;
047    
048        /**
049         * Creates a SpringConfiguration that registers and unregisters services that have been defined in a Spring ApplicationContext.
050         * @param applicationContext the application context from which services are registered
051         * @param kernel the kernel in which services are registered and unregistered
052         * @throws Exception if a problem occurs while registering the services from the application context
053         */
054        public SpringConfiguration(AbstractXmlApplicationContext applicationContext, Kernel kernel) throws Exception {
055            this.applicationContext = applicationContext;
056            this.kernel = kernel;
057    
058            ClassLoader classLoader = getClassLoader(applicationContext);
059            ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
060            Thread.currentThread().setContextClassLoader(classLoader);
061    
062            // read the configuration file from source
063            applicationContext.refresh();
064            
065            // If the class loader was modified by an xml preprocessor,
066            // make sure we have the right one
067            classLoader = getClassLoader(applicationContext);
068    
069            try {
070    
071                // build a map from bean name to service name
072                Map serviceNameIndex = buildServiceNameIndex(applicationContext);
073    
074                // Use Spring to create all of the beans
075                Map factories = new HashMap(serviceNameIndex.size());
076                for (Iterator iterator = serviceNameIndex.entrySet().iterator(); iterator.hasNext();) {
077                    Map.Entry entry = (Map.Entry) iterator.next();
078                    String beanName = (String) entry.getKey();
079                    ServiceName serviceName = (ServiceName) entry.getValue();
080    
081                    Object bean = applicationContext.getBean(beanName);
082                    StaticServiceFactory serviceFactory = new StaticServiceFactory(bean, classLoader);
083                    factories.put(serviceName, serviceFactory);
084                }
085                serviceFactories = Collections.unmodifiableMap(factories);
086    
087                // register each bean with the kernel
088                for (Iterator iterator = serviceFactories.entrySet().iterator(); iterator.hasNext();) {
089                    Map.Entry entry = (Map.Entry) iterator.next();
090                    ServiceName serviceName = (ServiceName) entry.getKey();
091                    StaticServiceFactory serviceFactory = (StaticServiceFactory) entry.getValue();
092                    kernel.registerService(serviceName, serviceFactory);
093                }
094    
095            } catch (ServiceAlreadyExistsException e) {
096                destroy();
097                throw e;
098            } catch (ServiceRegistrationException e) {
099                destroy();
100                throw e;
101            } finally {
102                Thread.currentThread().setContextClassLoader(oldClassLoader);
103            }
104        }
105    
106        /**
107         * Gets the unique identifier of this configuration.
108         * @return the unique identifier of this configuration
109         */
110        public String getId() {
111            return applicationContext.getDisplayName();
112        }
113    
114        /**
115         * Gets the service factories for the services defined in this configuration by ServiceName.
116         * @return the service factories for the services defined in this configuration by ServiceName
117         */
118        public Map getServiceFactories() {
119            return serviceFactories;
120        }
121    
122        /**
123         * Unregisters all of the services registered with the kernel in the constructor.
124         */
125        public void destroy() {
126            ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
127            Thread.currentThread().setContextClassLoader(getClassLoader(applicationContext));
128            try {
129                for (Iterator iterator = serviceFactories.keySet().iterator(); iterator.hasNext();) {
130                    ServiceName serviceName = (ServiceName) iterator.next();
131                    try {
132                        kernel.unregisterService(serviceName);
133                    } catch (ServiceNotFoundException e) {
134                        // Doesn't matter
135                    } catch (ServiceRegistrationException e) {
136                        // todo ignored...
137                    }
138                }
139    
140                applicationContext.close();
141            } finally {
142                Thread.currentThread().setContextClassLoader(oldClassLoader);
143            }
144        }
145    
146        private static Map buildServiceNameIndex(AbstractXmlApplicationContext applicationContext) {
147            BeanDefinitionRegistry registry = null;
148            if (applicationContext instanceof BeanDefinitionRegistry) {
149                registry = (BeanDefinitionRegistry) applicationContext;
150            } else if (applicationContext.getBeanFactory() instanceof BeanDefinitionRegistry) {
151                registry = (BeanDefinitionRegistry) applicationContext.getBeanFactory(); 
152            }
153            if (registry != null) {
154                String[] beanNames = registry.getBeanDefinitionNames();
155                Map serviceNameIndex = new HashMap(beanNames.length);
156                for (int i = 0; i < beanNames.length; i++) {
157                    String beanName = beanNames[i];
158                    BeanDefinition def = registry.getBeanDefinition(beanName);
159                    if (!def.isAbstract()) {
160                        ServiceName serviceName = new StringServiceName(beanName);
161                        serviceNameIndex.put(beanName, serviceName);
162                    }
163                }
164                return serviceNameIndex;
165            } else {
166                String[] beanNames = applicationContext.getBeanDefinitionNames();
167                Map serviceNameIndex = new HashMap(beanNames.length);
168                for (int i = 0; i < beanNames.length; i++) {
169                    String beanName = beanNames[i];
170                    ServiceName serviceName = new StringServiceName(beanName);
171                    serviceNameIndex.put(beanName, serviceName);
172                }
173                return serviceNameIndex;
174            }
175        }
176    
177        protected static ClassLoader getClassLoader(AbstractXmlApplicationContext applicationContext) {
178            ClassLoader classLoader = applicationContext.getClassLoader();
179            if (classLoader == null) {
180                classLoader = Thread.currentThread().getContextClassLoader();
181            }
182            if (classLoader == null) {
183                classLoader = SpringLoader.class.getClassLoader();
184            }
185            return classLoader;
186        }
187    }