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/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 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 LOG.debug("Starting Spring ApplicationContext: " + applicationContext.getId()); 159 applicationContext.start(); 160 161 postProcessContext(); 162 } 163 164 protected void doStop() throws Exception { 165 super.doStop(); 166 if (applicationContext != null) { 167 LOG.debug("Stopping Spring ApplicationContext: " + applicationContext.getId()); 168 IOHelper.close(applicationContext); 169 } 170 } 171 172 protected ProducerTemplate findOrCreateCamelTemplate() { 173 String[] names = getApplicationContext().getBeanNamesForType(ProducerTemplate.class); 174 if (names != null && names.length > 0) { 175 return getApplicationContext().getBean(names[0], ProducerTemplate.class); 176 } 177 if (getCamelContexts().isEmpty()) { 178 throw new IllegalArgumentException("No CamelContexts are available so cannot create a ProducerTemplate!"); 179 } 180 return getCamelContexts().get(0).createProducerTemplate(); 181 } 182 183 protected AbstractApplicationContext createDefaultApplicationContext() throws IOException { 184 // daisy chain the parent and additional contexts 185 ApplicationContext parentContext = getParentApplicationContext(); 186 parentContext = addAdditionalLocationsFromClasspath(parentContext); 187 188 // file based 189 if (getFileApplicationContextUri() != null) { 190 String[] args = getFileApplicationContextUri().split(";"); 191 192 if (parentContext != null) { 193 return new FileSystemXmlApplicationContext(args, parentContext); 194 } else { 195 return new FileSystemXmlApplicationContext(args); 196 } 197 } 198 199 // default to classpath based 200 String[] args = getApplicationContextUri().split(";"); 201 if (parentContext != null) { 202 return new ClassPathXmlApplicationContext(args, parentContext); 203 } else { 204 return new ClassPathXmlApplicationContext(args); 205 } 206 } 207 208 protected Map<String, CamelContext> getCamelContextMap() { 209 Map<String, SpringCamelContext> map = applicationContext.getBeansOfType(SpringCamelContext.class); 210 Set<Map.Entry<String, SpringCamelContext>> entries = map.entrySet(); 211 Map<String, CamelContext> answer = new HashMap<String, CamelContext>(); 212 for (Map.Entry<String, SpringCamelContext> entry : entries) { 213 String name = entry.getKey(); 214 CamelContext camelContext = entry.getValue(); 215 answer.put(name, camelContext); 216 } 217 return answer; 218 } 219 220 protected ModelFileGenerator createModelFileGenerator() throws JAXBException { 221 return new ModelFileGenerator(new CamelNamespaceHandler().getJaxbContext()); 222 } 223 224 protected ApplicationContext addAdditionalLocationsFromClasspath(ApplicationContext parentContext) throws IOException { 225 StringBuilder sb = new StringBuilder(); 226 227 Set<String> locations = new LinkedHashSet<String>(); 228 findLocations(locations, Main.class.getClassLoader()); 229 230 if (!locations.isEmpty()) { 231 LOG.info("Found locations for additional Spring XML files: {}", locations); 232 233 String[] locs = locations.toArray(new String[locations.size()]); 234 ClassPathXmlApplicationContext additionalContext; 235 if (parentContext != null) { 236 additionalContext = new ClassPathXmlApplicationContext(locs, parentContext); 237 } else { 238 additionalContext = new ClassPathXmlApplicationContext(locs); 239 } 240 // and we must start the app context as well 241 additionalContext.start(); 242 return additionalContext; 243 } else { 244 return null; 245 } 246 } 247 248 protected void findLocations(Set<String> locations, ClassLoader classLoader) throws IOException { 249 Enumeration<URL> resources = classLoader.getResources(LOCATION_PROPERTIES); 250 while (resources.hasMoreElements()) { 251 URL url = resources.nextElement(); 252 BufferedReader reader = IOHelper.buffered(new InputStreamReader(url.openStream(), UTF8)); 253 try { 254 while (true) { 255 String line = reader.readLine(); 256 if (line == null) { 257 break; 258 } 259 line = line.trim(); 260 if (line.startsWith("#") || line.length() == 0) { 261 continue; 262 } 263 locations.add(line); 264 } 265 } finally { 266 IOHelper.close(reader, null, LOG); 267 } 268 } 269 } 270 271}