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.servicemix.jbi.container;
018    
019    import java.lang.reflect.Method;
020    import java.util.Iterator;
021    import java.util.LinkedHashMap;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.Properties;
025    
026    import javax.jbi.JBIException;
027    import javax.jbi.component.Component;
028    
029    import org.apache.servicemix.components.util.ComponentAdaptor;
030    import org.apache.servicemix.jbi.framework.ComponentMBeanImpl;
031    import org.apache.xbean.spring.context.impl.NamespaceHelper;
032    import org.springframework.beans.BeanUtils;
033    import org.springframework.beans.BeansException;
034    import org.springframework.beans.factory.BeanFactory;
035    import org.springframework.beans.factory.BeanFactoryAware;
036    import org.springframework.beans.factory.DisposableBean;
037    import org.springframework.beans.factory.InitializingBean;
038    import org.springframework.context.ApplicationContext;
039    import org.springframework.context.ApplicationContextAware;
040    import org.springframework.core.io.support.PropertiesLoaderUtils;
041    import org.springframework.util.ClassUtils;
042    
043    /**
044     * An enhanced JBI container which adds some Spring helper methods for
045     * easier configuration through spring's XML configuration file.
046     *
047     * @org.apache.xbean.XBean element="container" rootElement="true"
048     * description="The ServiceMix JBI Container"
049     * 
050     * @version $Revision: 594535 $
051     */
052    public class SpringJBIContainer extends JBIContainer implements InitializingBean, DisposableBean, 
053                                                                    BeanFactoryAware, ApplicationContextAware {
054    
055        private String[] componentNames;
056        private ActivationSpec[] activationSpecs;
057        private BeanFactory beanFactory;
058        private ApplicationContext applicationContext;
059        private String[] deployArchives;
060        private DeploySupport[] deployments;
061        private Map components;
062        private Map endpoints;
063        private Runnable onShutDown;
064    
065        public void afterPropertiesSet() throws Exception {
066            init();
067    
068            // lets iterate through all the component names and register them
069            if (componentNames != null) {
070                for (int i = 0; i < componentNames.length; i++) {
071                    String componentName = componentNames[i];
072                    activateComponent(new ActivationSpec(componentName, lookupBean(componentName)));
073                }
074            }
075    
076            if (activationSpecs != null) {
077                for (int i = 0; i < activationSpecs.length; i++) {
078                    ActivationSpec activationSpec = activationSpecs[i];
079                    activateComponent(activationSpec);
080                }
081            }
082    
083            if (deployArchives != null) {
084                for (int i = 0; i < deployArchives.length; i++) {
085                    String archive = deployArchives[i];
086                    installArchive(archive);
087                }
088            }
089    
090            if (components != null) {
091                for (Iterator it = components.entrySet().iterator(); it.hasNext();) {
092                    Map.Entry e = (Map.Entry) it.next();
093                    if (!(e.getKey() instanceof String)) {
094                        throw new JBIException("Component must have a non null string name");
095                    }
096                    if (!(e.getValue() instanceof Component)) {
097                        throw new JBIException("Component is not a known component");
098                    }
099                    String name = (String) e.getKey();
100                    activateComponent((Component) e.getValue(), name);
101                    getComponent(name).init();
102                }
103            }
104    
105            if (endpoints != null) {
106                initEndpoints();
107            }
108    
109            if (deployments != null) {
110                for (DeploySupport deployment : deployments) {
111                    deployment.deploy(this);
112                }
113            }
114    
115            start();
116        }
117    
118        private void initEndpoints() throws Exception {
119            if (components == null) {
120                components = new LinkedHashMap();
121            }
122            Class componentClass = Class.forName("org.apache.servicemix.common.DefaultComponent");
123            Class endpointClass = Class.forName("org.apache.servicemix.common.Endpoint");
124            Method addEndpointMethod = componentClass.getDeclaredMethod("addEndpoint", new Class[] {endpointClass });
125            addEndpointMethod.setAccessible(true);
126            Method getEndpointClassesMethod = componentClass.getDeclaredMethod("getEndpointClasses", null);
127            getEndpointClassesMethod.setAccessible(true);
128            for (Iterator it = endpoints.entrySet().iterator(); it.hasNext();) {
129                Map.Entry e = (Map.Entry) it.next();
130                String key = (String) e.getKey();
131                List l = (List) e.getValue();
132                for (Iterator itEp = l.iterator(); itEp.hasNext();) {
133                    Object endpoint = itEp.next();
134                    Component c = null;
135                    if (key.length() > 0) {
136                        Component comp = (Component) components.get(key);
137                        if (comp == null) {
138                            throw new JBIException("Could not find component '" + key + "' specified for endpoint");
139                        }
140                        c = comp;
141                    } else {
142                        for (Iterator itCmp = components.values().iterator(); itCmp.hasNext();) {
143                            Component comp = (Component) itCmp.next();
144                            Class[] endpointClasses = (Class[]) getEndpointClassesMethod.invoke(comp, null);
145                            if (isKnownEndpoint(endpoint, endpointClasses)) {
146                                c = comp;
147                                break;
148                            }
149                        }
150                        if (c == null) {
151                            c = getComponentForEndpoint(getEndpointClassesMethod, endpoint);
152                            if (c == null) {
153                                throw new JBIException("Unable to find a component for endpoint class: " + endpoint.getClass());
154                            }
155                        }
156                    }
157                    addEndpointMethod.invoke(c, new Object[] {endpoint });
158                }
159            }
160        }
161    
162        private Component getComponentForEndpoint(Method getEndpointClassesMethod, Object endpoint) throws Exception {
163            Properties namespaces = PropertiesLoaderUtils.loadAllProperties("META-INF/spring.handlers");
164            for (Iterator itNs = namespaces.keySet().iterator(); itNs.hasNext();) {
165                String namespaceURI = (String) itNs.next();
166                String uri = NamespaceHelper.createDiscoveryPathName(namespaceURI);
167                Properties props = PropertiesLoaderUtils.loadAllProperties(uri);
168                String compClassName = props.getProperty("component");
169                if (compClassName != null) {
170                    Class compClass = ClassUtils.forName(compClassName);
171                    Component comp = (Component) BeanUtils.instantiateClass(compClass);
172                    Class[] endpointClasses = (Class[]) getEndpointClassesMethod.invoke(comp, null);
173                    if (isKnownEndpoint(endpoint, endpointClasses)) {
174                        String name = chooseComponentName(comp);
175                        activateComponent(comp, name);
176                        components.put(name, comp);
177                        return comp;
178                    }
179                }
180            }
181            return null;
182        }
183    
184        private String chooseComponentName(Object c) {
185            String className = c.getClass().getName();
186            if (className.startsWith("org.apache.servicemix.")) {
187                int idx1 = className.lastIndexOf('.');
188                int idx0 = className.lastIndexOf('.', idx1 - 1);
189                String name = "servicemix-" + className.substring(idx0 + 1, idx1);
190                if (registry.getComponent(name) == null) {
191                    return name;
192                }
193            }
194            return createComponentID();
195        }
196    
197        private boolean isKnownEndpoint(Object endpoint, Class[] knownClasses) {
198            if (knownClasses != null) {
199                for (int i = 0; i < knownClasses.length; i++) {
200                    if (knownClasses[i].isInstance(endpoint)) {
201                        return true;
202                    }
203                }
204            }
205            return false;
206        }
207    
208        public void stop() throws JBIException {
209            if (beanFactory instanceof DisposableBean) {
210                DisposableBean disposable = (DisposableBean) beanFactory;
211                try {
212                    disposable.destroy();
213                } catch (Exception e) {
214                    throw new JBIException("Failed to dispose of the Spring BeanFactory due to: " + e, e);
215                }
216            }
217            super.stop();
218        }
219    
220        /**
221         * Returns the compoment or POJO registered with the given component ID.
222         *
223         * @param id
224         * @return the Component
225         */
226        public Object getBean(String id) {
227            ComponentMBeanImpl component = getComponent(id);
228            Object bean = component != null ? component.getComponent() : null;
229            if (bean instanceof ComponentAdaptor) {
230                bean = ((ComponentAdaptor) bean).getLifeCycle();
231            }
232            return bean;
233        }
234    
235        // Properties
236        //-------------------------------------------------------------------------
237        /**
238         * @org.apache.xbean.Property hidden="true"
239         */
240        public BeanFactory getBeanFactory() {
241            return beanFactory;
242        }
243    
244        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
245            this.beanFactory = beanFactory;
246        }
247    
248        public String[] getComponentNames() {
249            return componentNames;
250        }
251    
252        public void setComponentNames(String[] componentNames) {
253            this.componentNames = componentNames;
254        }
255    
256        public ActivationSpec[] getActivationSpecs() {
257            return activationSpecs;
258        }
259    
260        public void setActivationSpecs(ActivationSpec[] activationSpecs) throws JBIException {
261            this.activationSpecs = activationSpecs;
262        }
263    
264        public String[] getDeployArchives() {
265            return deployArchives;
266        }
267    
268        public void setDeployArchives(String[] deployArchives) {
269            this.deployArchives = deployArchives;
270        }
271    
272        public DeploySupport[] getDeployments() {
273            return deployments;
274        }
275    
276        public void setDeployments(DeploySupport[] deployments) {
277            this.deployments = deployments;
278        }
279    
280        // Implementation methods
281        //-------------------------------------------------------------------------
282        protected Object lookupBean(String componentName) {
283            Object bean = beanFactory.getBean(componentName);
284            if (bean == null) {
285                throw new IllegalArgumentException("Component name: " + componentName + " is not found in the Spring BeanFactory");
286            }
287            return bean;
288        }
289    
290        /**
291         * @return
292         * @org.apache.xbean.Property hidden="true"
293         */
294        public ApplicationContext getApplicationContext() {
295            return applicationContext;
296        }
297    
298        public void setApplicationContext(ApplicationContext applicationContext) {
299            this.applicationContext = applicationContext;
300        }
301    
302        public void destroy() throws Exception {
303            super.shutDown();
304        }
305    
306        public void shutDown() throws JBIException {
307            if (onShutDown != null) {
308                onShutDown.run();
309            } else {
310                //no shutdown handler has been set
311                //shutting down the container ourselves
312                super.shutDown();
313            }
314        }
315    
316        /**
317         * Set a {@link Runnable} which can handle the shutdown of the container
318         * 
319         * @param runnable the shutdown handler
320         */
321        public void onShutDown(Runnable runnable) {
322            this.onShutDown = runnable;
323        }
324    
325        /**
326         * @org.apache.xbean.Map flat="true" keyName="name" 
327         */
328        public Map getComponents() {
329            return components;
330        }
331    
332        public void setComponents(Map components) {
333            this.components = components;
334        }
335    
336        /**
337         * @org.apache.xbean.Map flat="true" dups="always" keyName="component" defaultKey=""
338         */
339        public Map getEndpoints() {
340            return endpoints;
341        }
342    
343        public void setEndpoints(Map endpoints) {
344            this.endpoints = endpoints;
345        }
346    
347    }