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.spring.handler.CamelNamespaceHandler;
036import org.apache.camel.util.IOHelper;
037import org.apache.camel.view.ModelFileGenerator;
038import org.springframework.context.ApplicationContext;
039import org.springframework.context.support.AbstractApplicationContext;
040import org.springframework.context.support.ClassPathXmlApplicationContext;
041import 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")
055public 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}