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 }