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/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}