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 */ 017 package org.apache.camel.impl; 018 019 import java.io.IOException; 020 import java.util.ArrayList; 021 import java.util.Arrays; 022 import java.util.LinkedList; 023 import java.util.List; 024 import java.util.Map; 025 import java.util.Set; 026 import java.util.concurrent.CountDownLatch; 027 import java.util.concurrent.TimeUnit; 028 import java.util.concurrent.atomic.AtomicBoolean; 029 030 import javax.xml.bind.JAXBException; 031 032 import org.apache.camel.CamelContext; 033 import org.apache.camel.ProducerTemplate; 034 import org.apache.camel.builder.RouteBuilder; 035 import org.apache.camel.model.RouteDefinition; 036 import org.apache.camel.processor.interceptor.Debugger; 037 import org.apache.camel.util.ObjectHelper; 038 import org.apache.camel.view.ModelFileGenerator; 039 import org.apache.camel.view.RouteDotGenerator; 040 import org.apache.commons.logging.Log; 041 import org.apache.commons.logging.LogFactory; 042 043 /** 044 * @version $Revision: 759848 $ 045 */ 046 public abstract class MainSupport extends ServiceSupport { 047 protected static final Log LOG = LogFactory.getLog(MainSupport.class); 048 protected String dotOutputDir; 049 private final List<Option> options = new ArrayList<Option>(); 050 private final CountDownLatch latch = new CountDownLatch(1); 051 private final AtomicBoolean completed = new AtomicBoolean(false); 052 private long duration = -1; 053 private TimeUnit timeUnit = TimeUnit.MILLISECONDS; 054 private String routesOutputFile; 055 private boolean aggregateDot; 056 private boolean debug; 057 private boolean trace; 058 private List<RouteBuilder> routeBuilders = new ArrayList<RouteBuilder>(); 059 private final List<CamelContext> camelContexts = new ArrayList<CamelContext>(); 060 private ProducerTemplate camelTemplate; 061 062 protected MainSupport() { 063 addOption(new Option("h", "help", "Displays the help screen") { 064 protected void doProcess(String arg, LinkedList<String> remainingArgs) { 065 showOptions(); 066 completed(); 067 } 068 }); 069 addOption(new ParameterOption("o", "outdir", 070 "Sets the DOT output directory where the visual representations of the routes are generated", 071 "dot") { 072 protected void doProcess(String arg, String parameter, LinkedList<String> remainingArgs) { 073 setDotOutputDir(parameter); 074 } 075 }); 076 addOption(new ParameterOption("ad", "aggregate-dot", 077 "Aggregates all routes (in addition to individual route generation) into one context to create one monolithic DOT file for visual representations the entire system.", 078 "aggregate-dot") { 079 protected void doProcess(String arg, String parameter, LinkedList<String> remainingArgs) { 080 setAggregateDot("true".equals(parameter)); 081 } 082 }); 083 addOption(new ParameterOption("d", "duration", 084 "Sets the time duration that the applicaiton will run for, by default in milliseconds. You can use '10s' for 10 seconds etc", 085 "duration") { 086 protected void doProcess(String arg, String parameter, LinkedList<String> remainingArgs) { 087 String value = parameter.toUpperCase(); 088 if (value.endsWith("S")) { 089 value = value.substring(0, value.length() - 1); 090 setTimeUnit(TimeUnit.SECONDS); 091 } 092 setDuration(Integer.parseInt(value)); 093 } 094 }); 095 096 addOption(new Option("x", "debug", "Enables the debugger") { 097 protected void doProcess(String arg, LinkedList<String> remainingArgs) { 098 enableDebug(); 099 } 100 }); 101 addOption(new Option("t", "trace", "Enables tracing") { 102 protected void doProcess(String arg, LinkedList<String> remainingArgs) { 103 enableTrace(); 104 } 105 }); 106 addOption(new ParameterOption("out", "output", "Output all routes to the specified XML file", "filename") { 107 protected void doProcess(String arg, String parameter, 108 LinkedList<String> remainingArgs) { 109 setRoutesOutputFile(parameter); 110 } 111 }); 112 } 113 114 /** 115 * Runs this process with the given arguments 116 */ 117 public void run() { 118 if (!completed.get()) { 119 try { 120 start(); 121 waitUntilCompleted(); 122 stop(); 123 } catch (Exception e) { 124 LOG.error("Failed: " + e, e); 125 } 126 } 127 } 128 129 /** 130 * Marks this process as being completed 131 */ 132 public void completed() { 133 completed.set(true); 134 latch.countDown(); 135 } 136 137 /** 138 * Displays the command line options 139 */ 140 public void showOptions() { 141 showOptionsHeader(); 142 143 for (Option option : options) { 144 System.out.println(option.getInformation()); 145 } 146 } 147 148 /** 149 * Parses the command line arguments 150 */ 151 public void parseArguments(String[] arguments) { 152 LinkedList<String> args = new LinkedList<String>(Arrays.asList(arguments)); 153 154 boolean valid = true; 155 while (!args.isEmpty()) { 156 String arg = args.removeFirst(); 157 158 boolean handled = false; 159 for (Option option : options) { 160 if (option.processOption(arg, args)) { 161 handled = true; 162 break; 163 } 164 } 165 if (!handled) { 166 System.out.println("Unknown option: " + arg); 167 System.out.println(); 168 valid = false; 169 break; 170 } 171 } 172 if (!valid) { 173 showOptions(); 174 completed(); 175 } 176 } 177 178 public void addOption(Option option) { 179 options.add(option); 180 } 181 182 public long getDuration() { 183 return duration; 184 } 185 186 /** 187 * Sets the duration to run the application for in milliseconds until it 188 * should be terminated. Defaults to -1. Any value <= 0 will run forever. 189 */ 190 public void setDuration(long duration) { 191 this.duration = duration; 192 } 193 194 public TimeUnit getTimeUnit() { 195 return timeUnit; 196 } 197 198 /** 199 * Sets the time unit duration 200 */ 201 public void setTimeUnit(TimeUnit timeUnit) { 202 this.timeUnit = timeUnit; 203 } 204 205 public String getDotOutputDir() { 206 return dotOutputDir; 207 } 208 209 /** 210 * Sets the output directory of the generated DOT Files to show the visual 211 * representation of the routes. A null value disables the dot file 212 * generation 213 */ 214 public void setDotOutputDir(String dotOutputDir) { 215 this.dotOutputDir = dotOutputDir; 216 } 217 218 public void setAggregateDot(boolean aggregateDot) { 219 this.aggregateDot = aggregateDot; 220 } 221 222 public boolean isAggregateDot() { 223 return aggregateDot; 224 } 225 226 public boolean isDebug() { 227 return debug; 228 } 229 230 public void enableDebug() { 231 this.debug = true; 232 } 233 234 public boolean isTrace() { 235 return trace; 236 } 237 238 public void enableTrace() { 239 this.trace = true; 240 } 241 242 public void setRoutesOutputFile(String routesOutputFile) { 243 this.routesOutputFile = routesOutputFile; 244 } 245 246 public String getRoutesOutputFile() { 247 return routesOutputFile; 248 } 249 250 /** 251 * Returns the currently active debugger if one is enabled 252 * 253 * @return the current debugger or null if none is active 254 * @see #enableDebug() 255 */ 256 public Debugger getDebugger() { 257 for (CamelContext camelContext : camelContexts) { 258 Debugger debugger = Debugger.getDebugger(camelContext); 259 if (debugger != null) { 260 return debugger; 261 } 262 } 263 return null; 264 } 265 266 protected void doStop() throws Exception { 267 LOG.info("Apache Camel " + getVersion() + " stopping"); 268 // call completed to properly stop as we count down the waiting latch 269 completed(); 270 } 271 272 protected void doStart() throws Exception { 273 LOG.info("Apache Camel " + getVersion() + " starting"); 274 } 275 276 protected void waitUntilCompleted() { 277 while (!completed.get()) { 278 try { 279 if (duration > 0) { 280 TimeUnit unit = getTimeUnit(); 281 LOG.info("Waiting for: " + duration + " " + unit); 282 latch.await(duration, unit); 283 completed.set(true); 284 } else { 285 latch.await(); 286 } 287 } catch (InterruptedException e) { 288 Thread.currentThread().interrupt(); 289 } 290 } 291 } 292 293 /** 294 * Parses the command line arguments then runs the program 295 */ 296 public void run(String[] args) { 297 parseArguments(args); 298 run(); 299 } 300 301 /** 302 * Displays the header message for the command line options 303 */ 304 public void showOptionsHeader() { 305 System.out.println("Apache Camel Runner takes the following options"); 306 System.out.println(); 307 } 308 309 public List<CamelContext> getCamelContexts() { 310 return camelContexts; 311 } 312 313 public List<RouteBuilder> getRouteBuilders() { 314 return routeBuilders; 315 } 316 317 public void setRouteBuilders(List<RouteBuilder> routeBuilders) { 318 this.routeBuilders = routeBuilders; 319 } 320 321 public List<RouteDefinition> getRouteDefinitions() { 322 List<RouteDefinition> answer = new ArrayList<RouteDefinition>(); 323 for (CamelContext camelContext : camelContexts) { 324 answer.addAll(camelContext.getRouteDefinitions()); 325 } 326 return answer; 327 } 328 329 /** 330 * Returns a {@link org.apache.camel.ProducerTemplate} from the Spring {@link org.springframework.context.ApplicationContext} instances 331 * or lazily creates a new one dynamically 332 */ 333 public ProducerTemplate getCamelTemplate() { 334 if (camelTemplate == null) { 335 camelTemplate = findOrCreateCamelTemplate(); 336 } 337 return camelTemplate; 338 } 339 340 protected abstract ProducerTemplate findOrCreateCamelTemplate(); 341 342 protected abstract Map<String, CamelContext> getCamelContextMap(); 343 344 protected void postProcessContext() throws Exception { 345 Map<String, CamelContext> map = getCamelContextMap(); 346 Set<Map.Entry<String, CamelContext>> entries = map.entrySet(); 347 int size = entries.size(); 348 for (Map.Entry<String, CamelContext> entry : entries) { 349 String name = entry.getKey(); 350 CamelContext camelContext = entry.getValue(); 351 camelContexts.add(camelContext); 352 generateDot(name, camelContext, size); 353 postProcesCamelContext(camelContext); 354 } 355 356 if (isAggregateDot()) { 357 generateDot("aggregate", aggregateCamelContext(), 1); 358 } 359 360 if (!"".equals(getRoutesOutputFile())) { 361 outputRoutesToFile(); 362 } 363 } 364 365 protected void outputRoutesToFile() throws IOException, JAXBException { 366 if (ObjectHelper.isNotEmpty(getRoutesOutputFile())) { 367 LOG.info("Generating routes as XML in the file named: " + getRoutesOutputFile()); 368 ModelFileGenerator generator = createModelFileGenerator(); 369 generator.marshalRoutesUsingJaxb(getRoutesOutputFile(), getRouteDefinitions()); 370 } 371 } 372 373 protected abstract ModelFileGenerator createModelFileGenerator() throws JAXBException; 374 375 protected void generateDot(String name, CamelContext camelContext, int size) throws IOException { 376 String outputDir = dotOutputDir; 377 if (ObjectHelper.isNotEmpty(outputDir)) { 378 if (size > 1) { 379 outputDir += "/" + name; 380 } 381 RouteDotGenerator generator = new RouteDotGenerator(outputDir); 382 LOG.info("Generating DOT file for routes: " + outputDir + " for: " + camelContext + " with name: " + name); 383 generator.drawRoutes(camelContext); 384 } 385 } 386 387 /** 388 * Used for aggregate dot generation, generate a single camel context containing all of the available contexts 389 */ 390 private CamelContext aggregateCamelContext() throws Exception { 391 if (camelContexts.size() == 1) { 392 return camelContexts.get(0); 393 } else { 394 CamelContext answer = new DefaultCamelContext(); 395 for (CamelContext camelContext : camelContexts) { 396 answer.addRouteDefinitions(camelContext.getRouteDefinitions()); 397 } 398 return answer; 399 } 400 } 401 402 protected void postProcesCamelContext(CamelContext camelContext) throws Exception { 403 for (RouteBuilder routeBuilder : routeBuilders) { 404 camelContext.addRoutes(routeBuilder); 405 } 406 } 407 408 public void addRouteBuilder(RouteBuilder routeBuilder) { 409 getRouteBuilders().add(routeBuilder); 410 } 411 412 public abstract class Option { 413 private String abbreviation; 414 private String fullName; 415 private String description; 416 417 protected Option(String abbreviation, String fullName, String description) { 418 this.abbreviation = "-" + abbreviation; 419 this.fullName = "-" + fullName; 420 this.description = description; 421 } 422 423 public boolean processOption(String arg, LinkedList<String> remainingArgs) { 424 if (arg.equalsIgnoreCase(abbreviation) || fullName.startsWith(arg)) { 425 doProcess(arg, remainingArgs); 426 return true; 427 } 428 return false; 429 } 430 431 public String getAbbreviation() { 432 return abbreviation; 433 } 434 435 public String getDescription() { 436 return description; 437 } 438 439 public String getFullName() { 440 return fullName; 441 } 442 443 public String getInformation() { 444 return " " + getAbbreviation() + " or " + getFullName() + " = " + getDescription(); 445 } 446 447 protected abstract void doProcess(String arg, LinkedList<String> remainingArgs); 448 } 449 450 public abstract class ParameterOption extends Option { 451 private String parameterName; 452 453 protected ParameterOption(String abbreviation, String fullName, String description, 454 String parameterName) { 455 super(abbreviation, fullName, description); 456 this.parameterName = parameterName; 457 } 458 459 protected void doProcess(String arg, LinkedList<String> remainingArgs) { 460 if (remainingArgs.isEmpty()) { 461 System.err.println("Expected fileName for "); 462 showOptions(); 463 completed(); 464 } else { 465 String parameter = remainingArgs.removeFirst(); 466 doProcess(arg, parameter, remainingArgs); 467 } 468 } 469 470 public String getInformation() { 471 return " " + getAbbreviation() + " or " + getFullName() 472 + " <" + parameterName + "> = " + getDescription(); 473 } 474 475 protected abstract void doProcess(String arg, String parameter, LinkedList<String> remainingArgs); 476 } 477 }