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}