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