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