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