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: 67260 $ 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 public void setDuration(long duration) { 192 this.duration = duration; 193 } 194 195 public TimeUnit getTimeUnit() { 196 return timeUnit; 197 } 198 199 /** 200 * Sets the time unit duration 201 */ 202 public void setTimeUnit(TimeUnit timeUnit) { 203 this.timeUnit = timeUnit; 204 } 205 206 public String getDotOutputDir() { 207 return dotOutputDir; 208 } 209 210 /** 211 * Sets the output directory of the generated DOT Files to show the visual 212 * representation of the routes. A null value disables the dot file 213 * generation 214 */ 215 public void setDotOutputDir(String dotOutputDir) { 216 this.dotOutputDir = dotOutputDir; 217 } 218 219 public void setAggregateDot(boolean aggregateDot) { 220 this.aggregateDot = aggregateDot; 221 } 222 223 public boolean isAggregateDot() { 224 return aggregateDot; 225 } 226 227 public boolean isDebug() { 228 return debug; 229 } 230 231 public void enableDebug() { 232 this.debug = true; 233 } 234 235 public boolean isTrace() { 236 return trace; 237 } 238 239 public void enableTrace() { 240 this.trace = true; 241 } 242 243 public void setRoutesOutputFile(String routesOutputFile) { 244 this.routesOutputFile = routesOutputFile; 245 } 246 247 public String getRoutesOutputFile() { 248 return routesOutputFile; 249 } 250 251 /** 252 * Returns the currently active debugger if one is enabled 253 * 254 * @return the current debugger or null if none is active 255 * @see #enableDebug() 256 */ 257 public Debugger getDebugger() { 258 for (CamelContext camelContext : camelContexts) { 259 Debugger debugger = Debugger.getDebugger(camelContext); 260 if (debugger != null) { 261 return debugger; 262 } 263 } 264 return null; 265 } 266 267 protected void doStop() throws Exception { 268 LOG.info("Apache Camel " + getVersion() + " stopping"); 269 // call completed to properly stop as we count down the waiting latch 270 completed(); 271 } 272 273 protected void doStart() throws Exception { 274 LOG.info("Apache Camel " + getVersion() + " starting"); 275 } 276 277 protected void waitUntilCompleted() { 278 while (!completed.get()) { 279 try { 280 if (duration > 0) { 281 TimeUnit unit = getTimeUnit(); 282 LOG.info("Waiting for: " + duration + " " + unit); 283 latch.await(duration, unit); 284 completed.set(true); 285 } else { 286 latch.await(); 287 } 288 } catch (InterruptedException e) { 289 Thread.currentThread().interrupt(); 290 } 291 } 292 } 293 294 /** 295 * Parses the command line arguments then runs the program 296 */ 297 public void run(String[] args) { 298 parseArguments(args); 299 run(); 300 } 301 302 /** 303 * Displays the header message for the command line options 304 */ 305 public void showOptionsHeader() { 306 System.out.println("Apache Camel Runner takes the following options"); 307 System.out.println(); 308 } 309 310 public List<CamelContext> getCamelContexts() { 311 return camelContexts; 312 } 313 314 public List<RouteBuilder> getRouteBuilders() { 315 return routeBuilders; 316 } 317 318 public void setRouteBuilders(List<RouteBuilder> routeBuilders) { 319 this.routeBuilders = routeBuilders; 320 } 321 322 public List<RouteType> getRouteDefinitions() { 323 List<RouteType> answer = new ArrayList<RouteType>(); 324 for (CamelContext camelContext : camelContexts) { 325 answer.addAll(camelContext.getRouteDefinitions()); 326 } 327 return answer; 328 } 329 330 /** 331 * Returns a {@link org.apache.camel.ProducerTemplate} from the Spring {@link org.springframework.context.ApplicationContext} instances 332 * or lazily creates a new one dynamically 333 */ 334 public ProducerTemplate getCamelTemplate() { 335 if (camelTemplate == null) { 336 camelTemplate = findOrCreateCamelTemplate(); 337 } 338 return camelTemplate; 339 } 340 341 protected abstract ProducerTemplate findOrCreateCamelTemplate(); 342 343 protected abstract Map<String, CamelContext> getCamelContextMap(); 344 345 protected void postProcessContext() throws Exception { 346 Map<String, CamelContext> map = getCamelContextMap(); 347 Set<Map.Entry<String, CamelContext>> entries = map.entrySet(); 348 int size = entries.size(); 349 for (Map.Entry<String, CamelContext> entry : entries) { 350 String name = entry.getKey(); 351 CamelContext camelContext = entry.getValue(); 352 camelContexts.add(camelContext); 353 generateDot(name, camelContext, size); 354 postProcesCamelContext(camelContext); 355 } 356 357 if (isAggregateDot()) { 358 generateDot("aggregate", aggregateCamelContext(), 1); 359 } 360 361 if (!"".equals(getRoutesOutputFile())) { 362 outputRoutesToFile(); 363 } 364 } 365 366 protected void outputRoutesToFile() throws IOException, JAXBException { 367 if (ObjectHelper.isNotNullAndNonEmpty(getRoutesOutputFile())) { 368 LOG.info("Generating routes as XML in the file named: " + getRoutesOutputFile()); 369 ModelFileGenerator generator = createModelFileGenerator(); 370 generator.marshalRoutesUsingJaxb(getRoutesOutputFile(), getRouteDefinitions()); 371 } 372 } 373 374 protected abstract ModelFileGenerator createModelFileGenerator() throws JAXBException; 375 376 protected void generateDot(String name, CamelContext camelContext, int size) throws IOException { 377 String outputDir = dotOutputDir; 378 if (ObjectHelper.isNotNullAndNonEmpty(outputDir)) { 379 if (size > 1) { 380 outputDir += "/" + name; 381 } 382 RouteDotGenerator generator = new RouteDotGenerator(outputDir); 383 LOG.info("Generating DOT file for routes: " + outputDir + " for: " + camelContext + " with name: " + name); 384 generator.drawRoutes(camelContext); 385 } 386 } 387 388 /** 389 * Used for aggregate dot generation, generate a single camel context containing all of the available contexts 390 */ 391 private CamelContext aggregateCamelContext() throws Exception { 392 if (camelContexts.size() == 1) { 393 return camelContexts.get(0); 394 } else { 395 DefaultCamelContext answer = new DefaultCamelContext(); 396 for (CamelContext camelContext : camelContexts) { 397 answer.addRouteDefinitions(camelContext.getRouteDefinitions()); 398 } 399 return answer; 400 } 401 } 402 403 protected void postProcesCamelContext(CamelContext camelContext) throws Exception { 404 for (RouteBuilder routeBuilder : routeBuilders) { 405 camelContext.addRoutes(routeBuilder); 406 } 407 } 408 409 public void addRouteBuilder(RouteBuilder routeBuilder) { 410 getRouteBuilders().add(routeBuilder); 411 } 412 413 public abstract class Option { 414 private String abbreviation; 415 private String fullName; 416 private String description; 417 418 protected Option(String abbreviation, String fullName, String description) { 419 this.abbreviation = "-" + abbreviation; 420 this.fullName = "-" + fullName; 421 this.description = description; 422 } 423 424 public boolean processOption(String arg, LinkedList<String> remainingArgs) { 425 if (arg.equalsIgnoreCase(abbreviation) || fullName.startsWith(arg)) { 426 doProcess(arg, remainingArgs); 427 return true; 428 } 429 return false; 430 } 431 432 public String getAbbreviation() { 433 return abbreviation; 434 } 435 436 public String getDescription() { 437 return description; 438 } 439 440 public String getFullName() { 441 return fullName; 442 } 443 444 public String getInformation() { 445 return " " + getAbbreviation() + " or " + getFullName() + " = " + getDescription(); 446 } 447 448 protected abstract void doProcess(String arg, LinkedList<String> remainingArgs); 449 } 450 451 public abstract class ParameterOption extends Option { 452 private String parameterName; 453 454 protected ParameterOption(String abbreviation, String fullName, String description, 455 String parameterName) { 456 super(abbreviation, fullName, description); 457 this.parameterName = parameterName; 458 } 459 460 protected void doProcess(String arg, LinkedList<String> remainingArgs) { 461 if (remainingArgs.isEmpty()) { 462 System.err.println("Expected fileName for "); 463 showOptions(); 464 completed(); 465 } else { 466 String parameter = remainingArgs.removeFirst(); 467 doProcess(arg, parameter, remainingArgs); 468 } 469 } 470 471 public String getInformation() { 472 return " " + getAbbreviation() + " or " + getFullName() 473 + " <" + parameterName + "> = " + getDescription(); 474 } 475 476 protected abstract void doProcess(String arg, String parameter, LinkedList<String> remainingArgs); 477 } 478 }