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