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 }