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.camel.spring;
018    
019    import java.io.BufferedReader;
020    import java.io.IOException;
021    import java.io.InputStreamReader;
022    import java.net.URL;
023    import java.nio.charset.Charset;
024    import java.util.Enumeration;
025    import java.util.HashMap;
026    import java.util.LinkedHashSet;
027    import java.util.LinkedList;
028    import java.util.Map;
029    import java.util.Set;
030    import javax.xml.bind.JAXBException;
031    
032    import org.apache.camel.CamelContext;
033    import org.apache.camel.ProducerTemplate;
034    import org.apache.camel.impl.MainSupport;
035    import org.apache.camel.spring.handler.CamelNamespaceHandler;
036    import org.apache.camel.util.IOHelper;
037    import org.apache.camel.view.ModelFileGenerator;
038    import org.springframework.context.ApplicationContext;
039    import org.springframework.context.support.AbstractApplicationContext;
040    import org.springframework.context.support.ClassPathXmlApplicationContext;
041    import org.springframework.context.support.FileSystemXmlApplicationContext;
042    
043    /**
044     * A command line tool for booting up a CamelContext using an optional Spring
045     * {@link org.springframework.context.ApplicationContext}.
046     * <p/>
047     * By placing a file in the {@link #LOCATION_PROPERTIES} directory of any JARs on the classpath,
048     * allows this Main class to load those additional Spring XML files as Spring
049     * {@link org.springframework.context.ApplicationContext} to be included.
050     * <p/>
051     * Each line in the {@link #LOCATION_PROPERTIES} is a reference to a Spring XML file to include,
052     * which by default gets loaded from classpath.
053     */
054    @SuppressWarnings("deprecation")
055    public class Main extends MainSupport {
056    
057        public static final String LOCATION_PROPERTIES = "META-INF/camel-spring/location.properties";
058        protected static Main instance;
059        private static final Charset UTF8 = Charset.forName("UTF-8");
060    
061        private String applicationContextUri = "META-INF/spring/*.xml";
062        private String fileApplicationContextUri;
063        private AbstractApplicationContext applicationContext;
064        private AbstractApplicationContext parentApplicationContext;
065        private AbstractApplicationContext additionalApplicationContext;
066        private String parentApplicationContextUri;
067    
068        public Main() {
069    
070            addOption(new ParameterOption("ac", "applicationContext",
071                    "Sets the classpath based spring ApplicationContext", "applicationContext") {
072                protected void doProcess(String arg, String parameter, LinkedList<String> remainingArgs) {
073                    setApplicationContextUri(parameter);
074                }
075            });
076    
077            addOption(new ParameterOption("fa", "fileApplicationContext",
078                    "Sets the filesystem based spring ApplicationContext", "fileApplicationContext") {
079                protected void doProcess(String arg, String parameter, LinkedList<String> remainingArgs) {
080                    setFileApplicationContextUri(parameter);
081                }
082            });
083    
084        }
085    
086        public static void main(String... args) throws Exception {
087            Main main = new Main();
088            instance = main;
089            main.enableHangupSupport();
090            main.run(args);
091        }
092    
093        /**
094         * Returns the currently executing main
095         *
096         * @return the current running instance
097         */
098        public static Main getInstance() {
099            return instance;
100        }
101    
102        // Properties
103        // -------------------------------------------------------------------------
104        public AbstractApplicationContext getApplicationContext() {
105            return applicationContext;
106        }
107    
108        public void setApplicationContext(AbstractApplicationContext applicationContext) {
109            this.applicationContext = applicationContext;
110        }
111    
112        public String getApplicationContextUri() {
113            return applicationContextUri;
114        }
115    
116        public void setApplicationContextUri(String applicationContextUri) {
117            this.applicationContextUri = applicationContextUri;
118        }
119    
120        public String getFileApplicationContextUri() {
121            return fileApplicationContextUri;
122        }
123    
124        public void setFileApplicationContextUri(String fileApplicationContextUri) {
125            this.fileApplicationContextUri = fileApplicationContextUri;
126        }
127    
128        public AbstractApplicationContext getParentApplicationContext() {
129            if (parentApplicationContext == null) {
130                if (parentApplicationContextUri != null) {
131                    parentApplicationContext = new ClassPathXmlApplicationContext(parentApplicationContextUri);
132                    parentApplicationContext.start();
133                }
134            }
135            return parentApplicationContext;
136        }
137    
138        public void setParentApplicationContext(AbstractApplicationContext parentApplicationContext) {
139            this.parentApplicationContext = parentApplicationContext;
140        }
141    
142        public String getParentApplicationContextUri() {
143            return parentApplicationContextUri;
144        }
145    
146        public void setParentApplicationContextUri(String parentApplicationContextUri) {
147            this.parentApplicationContextUri = parentApplicationContextUri;
148        }
149    
150        // Implementation methods
151        // -------------------------------------------------------------------------
152    
153        @Override
154        protected void doStart() throws Exception {
155            super.doStart();
156            if (applicationContext == null) {
157                applicationContext = createDefaultApplicationContext();
158            }
159    
160            // then start any additional after Camel has been started
161            if (additionalApplicationContext == null) {
162                additionalApplicationContext = createAdditionalLocationsFromClasspath();
163                if (additionalApplicationContext != null) {
164                    LOG.debug("Starting Additional ApplicationContext: " + additionalApplicationContext.getId());
165                    additionalApplicationContext.start();
166                }
167            }
168    
169            LOG.debug("Starting Spring ApplicationContext: " + applicationContext.getId());
170            applicationContext.start();
171    
172            postProcessContext();
173        }
174    
175        protected void doStop() throws Exception {
176            super.doStop();
177            if (additionalApplicationContext != null) {
178                LOG.debug("Stopping Additional ApplicationContext: " + additionalApplicationContext.getId());
179                IOHelper.close(additionalApplicationContext);
180            }
181            if (applicationContext != null) {
182                LOG.debug("Stopping Spring ApplicationContext: " + applicationContext.getId());
183                IOHelper.close(applicationContext);
184            }
185        }
186    
187        protected ProducerTemplate findOrCreateCamelTemplate() {
188            String[] names = getApplicationContext().getBeanNamesForType(ProducerTemplate.class);
189            if (names != null && names.length > 0) {
190                return getApplicationContext().getBean(names[0], ProducerTemplate.class);
191            }
192            if (getCamelContexts().isEmpty()) {
193                throw new IllegalArgumentException("No CamelContexts are available so cannot create a ProducerTemplate!");
194            }
195            return getCamelContexts().get(0).createProducerTemplate();
196        }
197    
198        protected AbstractApplicationContext createDefaultApplicationContext() throws IOException {
199            ApplicationContext parentContext = getParentApplicationContext();
200    
201            // file based
202            if (getFileApplicationContextUri() != null) {
203                String[] args = getFileApplicationContextUri().split(";");
204    
205                if (parentContext != null) {
206                    return new FileSystemXmlApplicationContext(args, parentContext);
207                } else {
208                    return new FileSystemXmlApplicationContext(args);
209                }
210            }
211    
212            // default to classpath based
213            String[] args = getApplicationContextUri().split(";");
214            if (parentContext != null) {
215                return new ClassPathXmlApplicationContext(args, parentContext);
216            } else {
217                return new ClassPathXmlApplicationContext(args);
218            }
219        }
220    
221        protected Map<String, CamelContext> getCamelContextMap() {
222            Map<String, SpringCamelContext> map = applicationContext.getBeansOfType(SpringCamelContext.class);
223            Set<Map.Entry<String, SpringCamelContext>> entries = map.entrySet();
224            Map<String, CamelContext> answer = new HashMap<String, CamelContext>();
225            for (Map.Entry<String, SpringCamelContext> entry : entries) {
226                String name = entry.getKey();
227                CamelContext camelContext = entry.getValue();
228                answer.put(name, camelContext);
229            }
230            return answer;
231        }
232    
233        protected ModelFileGenerator createModelFileGenerator() throws JAXBException {
234            return new ModelFileGenerator(new CamelNamespaceHandler().getJaxbContext());
235        }
236    
237        protected AbstractApplicationContext createAdditionalLocationsFromClasspath() throws IOException {
238            Set<String> locations = new LinkedHashSet<String>();
239            findLocations(locations, Main.class.getClassLoader());
240    
241            if (!locations.isEmpty()) {
242                LOG.info("Found locations for additional Spring XML files: {}", locations);
243    
244                String[] locs = locations.toArray(new String[locations.size()]);
245                return new ClassPathXmlApplicationContext(locs);
246            } else {
247                return null;
248            }
249        }
250    
251        protected void findLocations(Set<String> locations, ClassLoader classLoader) throws IOException {
252            Enumeration<URL> resources = classLoader.getResources(LOCATION_PROPERTIES);
253            while (resources.hasMoreElements()) {
254                URL url = resources.nextElement();
255                BufferedReader reader = IOHelper.buffered(new InputStreamReader(url.openStream(), UTF8));
256                try {
257                    while (true) {
258                        String line = reader.readLine();
259                        if (line == null) {
260                            break;
261                        }
262                        line = line.trim();
263                        if (line.startsWith("#") || line.length() == 0) {
264                            continue;
265                        }
266                        locations.add(line);
267                    }
268                } finally {
269                    IOHelper.close(reader, null, LOG);
270                }
271            }
272        }
273    
274    }