| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.camel.maven; |
| |
| import java.io.BufferedReader; |
| import java.io.Closeable; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.ResourceBundle; |
| import java.util.Set; |
| |
| import org.apache.maven.artifact.DependencyResolutionRequiredException; |
| import org.apache.maven.doxia.sink.Sink; |
| import org.apache.maven.doxia.siterenderer.Renderer; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugin.logging.Log; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.reporting.AbstractMavenReport; |
| import org.apache.maven.reporting.MavenReportException; |
| import org.codehaus.plexus.util.cli.CommandLineException; |
| import org.codehaus.plexus.util.cli.CommandLineUtils; |
| import org.codehaus.plexus.util.cli.Commandline; |
| |
| /** |
| * Runs Camel embedded with META-INF/services/*.xml spring files to try create |
| * DOT files for the routing rules, then converts the DOT files into another |
| * format such as PNG |
| * |
| * @version |
| * @goal dot |
| * @requiresDependencyResolution test |
| * @phase prepare-package |
| * @execute phase="test-compile" |
| * @see <a href="http://www.graphviz.org/">GraphViz</a> |
| */ |
| public class DotMojo extends AbstractMavenReport { |
| public static final String[] DEFAULT_GRAPHVIZ_OUTPUT_TYPES = {"png", "svg", "cmapx"}; |
| /** |
| * Subdirectory for report. |
| */ |
| protected static final String SUBDIRECTORY = "cameldoc"; |
| // |
| // For running Camel embedded |
| // ------------------------------------------------------------------------- |
| // |
| /** |
| * The duration to run the application for which by default is in |
| * milliseconds. A value <= 0 will run forever. |
| * Adding a s indicates seconds - eg "5s" means 5 seconds. |
| * |
| * @parameter expression="2s" |
| */ |
| protected String duration; |
| |
| /** |
| * Whether we should boot up camel with the META-INF/services/*.xml to |
| * generate the DOT file |
| * |
| * @parameter expression="true" |
| */ |
| protected boolean runCamel; |
| |
| /** |
| * Should we try run the DOT executable on the generated .DOT file to |
| * generate images |
| * |
| * @parameter expression="true" |
| */ |
| protected boolean useDot; |
| |
| /** |
| * The classpath based application context uri that spring wants to get. |
| * |
| * @parameter expression="${camel.applicationContextUri}" |
| */ |
| protected String applicationContextUri; |
| |
| /** |
| * The filesystem based application context uri that spring wants to get. |
| * |
| * @parameter expression="${camel.fileApplicationContextUri}" |
| */ |
| protected String fileApplicationContextUri; |
| |
| /** |
| * The main class to execute. |
| * |
| * @parameter expression="${camel.mainClass}" |
| * default-value="org.apache.camel.spring.Main" |
| * @required |
| */ |
| private String mainClass; |
| |
| /** |
| * Reference to Maven 2 Project. |
| * |
| * @parameter expression="${project}" |
| * @required |
| * @readonly |
| */ |
| private MavenProject project; |
| |
| /** |
| * Base output directory. |
| * |
| * @parameter expression="${project.build.directory}" |
| * @required |
| */ |
| private File buildDirectory; |
| |
| /** |
| * Base output directory for reports. |
| * |
| * @parameter default-value="${project.build.directory}/site/cameldoc" |
| * @required |
| */ |
| private File outputDirectory; |
| |
| |
| /** |
| * In the case of multiple camel contexts, setting aggregate == true will |
| * aggregate all into a monolithic context, otherwise they will be processed |
| * independently. |
| * |
| * @parameter |
| */ |
| private String aggregate; |
| |
| /** |
| * GraphViz executable location; visualization (images) will be generated |
| * only if you install this program and set this property to the executable |
| * dot (dot.exe on Win). |
| * |
| * @parameter expression="dot" |
| */ |
| private String executable; |
| |
| /** |
| * Graphviz output types. Default is png. Possible values: png, jpg, gif, |
| * svg. |
| * |
| * @required |
| */ |
| private String graphvizOutputType; |
| |
| /** |
| * Graphviz output types. Possible values: png, jpg, gif, svg. |
| * |
| * @parameter |
| */ |
| private String[] graphvizOutputTypes; |
| |
| /** |
| * Doxia SiteRender. |
| * |
| * @component |
| */ |
| private Renderer renderer; |
| |
| private String indexHtmlContent; |
| |
| /** |
| * @param locale report locale. |
| * @return report description. |
| * @see org.apache.maven.reporting.MavenReport#getDescription(Locale) |
| */ |
| public String getDescription(final Locale locale) { |
| return getBundle(locale).getString("report.dot.description"); |
| } |
| |
| /** |
| * @see org.apache.maven.reporting.MavenReport#getName(Locale) |
| */ |
| public String getName(final Locale locale) { |
| return getBundle(locale).getString("report.dot.name"); |
| } |
| |
| public String getOutputName() { |
| return SUBDIRECTORY + "/index"; |
| } |
| |
| public String getAggregate() { |
| return aggregate; |
| } |
| |
| public void setAggregate(String aggregate) { |
| this.aggregate = aggregate; |
| } |
| |
| public boolean isUseDot() { |
| return useDot; |
| } |
| |
| public void setUseDot(boolean useDot) { |
| this.useDot = useDot; |
| } |
| |
| public void execute() throws MojoExecutionException { |
| this.execute(this.buildDirectory, Locale.getDefault()); |
| try { |
| writeIndexHtmlFile(outputDirectory, "index.html", indexHtmlContent); |
| } catch (IOException e) { |
| throw new MojoExecutionException("Failed: " + e, e); |
| } |
| } |
| |
| protected void executeReport(final Locale locale) throws MavenReportException { |
| try { |
| this.execute(this.outputDirectory, locale); |
| |
| Sink kitchenSink = getSink(); |
| if (kitchenSink != null) { |
| kitchenSink.rawText(indexHtmlContent); |
| } else { |
| writeIndexHtmlFile(outputDirectory, "index.html", indexHtmlContent); |
| } |
| } catch (Exception e) { |
| final MavenReportException ex = new MavenReportException(e.getMessage()); |
| ex.initCause(e.getCause()); |
| throw ex; |
| } |
| } |
| |
| /** |
| * Executes DOT generator. |
| * |
| * @param outputDir report output directory. |
| * @param locale report locale. |
| * @throws MojoExecutionException if there were any execution errors. |
| */ |
| protected void execute(final File outputDir, final Locale locale) throws MojoExecutionException { |
| |
| try { |
| runCamelEmbedded(outputDir); |
| } catch (DependencyResolutionRequiredException e) { |
| throw new MojoExecutionException("Failed: " + e, e); |
| } |
| outputDir.mkdirs(); |
| |
| List<File> files = new ArrayList<File>(); |
| appendFiles(files, outputDirectory); |
| |
| if (graphvizOutputTypes == null) { |
| if (graphvizOutputType == null) { |
| graphvizOutputTypes = DEFAULT_GRAPHVIZ_OUTPUT_TYPES; |
| } else { |
| graphvizOutputTypes = new String[] {graphvizOutputType}; |
| } |
| } |
| try { |
| Set<String> contextNames = new HashSet<String>(); |
| for (File file : files) { |
| String contextName = file.getParentFile().getName(); |
| contextNames.add(contextName); |
| } |
| |
| boolean multipleCamelContexts = contextNames.size() > 1; |
| int size = files.size(); |
| for (int i = 0; i < size; i++) { |
| File file = files.get(i); |
| String contextName = null; |
| if (multipleCamelContexts) { |
| contextName = file.getParentFile().getName(); |
| } |
| |
| getLog().info("Generating contextName: " + contextName + " file: " + file + ""); |
| |
| generate(i, file, contextName); |
| } |
| |
| if (multipleCamelContexts) { |
| // lets generate an index page which lists each indiviual |
| // CamelContext file |
| StringWriter buffer = new StringWriter(); |
| PrintWriter out = new PrintWriter(buffer); |
| |
| out.println("<h1>Camel Contexts</h1>"); |
| out.println(); |
| |
| out.println("<ul>"); |
| for (String contextName : contextNames) { |
| out.print(" <li><a href='"); |
| out.print(contextName); |
| out.print("/routes.html'>"); |
| out.print(contextName); |
| out.println("</a></li>"); |
| } |
| out.println("</ul>"); |
| indexHtmlContent = buffer.toString(); |
| } |
| } catch (CommandLineException e) { |
| throw new MojoExecutionException("Failed: " + e, e); |
| } catch (IOException e) { |
| throw new MojoExecutionException("Failed: " + e, e); |
| } |
| } |
| |
| private void generate(int index, File file, String contextName) throws CommandLineException, |
| MojoExecutionException, IOException { |
| |
| StringWriter buffer = new StringWriter(); |
| PrintWriter out = new PrintWriter(buffer); |
| printHtmlHeader(out, contextName); |
| printHtmlFileHeader(out, file); |
| for (int j = 0; j < graphvizOutputTypes.length; j++) { |
| String format = graphvizOutputTypes[j]; |
| String generated = convertFile(file, format); |
| |
| if (format.equals("cmapx") && generated != null) { |
| // lets include the generated file inside the html |
| addFileToBuffer(out, new File(generated)); |
| } |
| } |
| printHtmlFileFooter(out, file); |
| printHtmlFooter(out); |
| |
| String content = buffer.toString(); |
| String name = file.getName(); |
| if (name.equalsIgnoreCase("routes.dot") || index == 0) { |
| indexHtmlContent = content; |
| } |
| int idx = name.lastIndexOf("."); |
| if (idx >= 0) { |
| name = name.substring(0, idx); |
| name += ".html"; |
| } |
| writeIndexHtmlFile(file.getParentFile(), name, content); |
| } |
| |
| protected void runCamelEmbedded(File outputDir) throws DependencyResolutionRequiredException { |
| if (runCamel) { |
| // default path, but can be overridden by configuration |
| if (applicationContextUri != null) { |
| getLog().info("Running Camel embedded to load Spring XML files from classpath: " + applicationContextUri); |
| } else if (fileApplicationContextUri != null) { |
| getLog().info("Running Camel embedded to load Spring XML files from file path: " + fileApplicationContextUri); |
| } else { |
| getLog().info("Running Camel embedded to load Spring XML files from default path: META-INF/spring/*.xml"); |
| } |
| |
| List list = project.getTestClasspathElements(); |
| getLog().debug("Using classpath: " + list); |
| |
| EmbeddedMojo mojo = new EmbeddedMojo(); |
| mojo.setClasspathElements(list); |
| mojo.setDotEnabled(true); |
| mojo.setMainClass(mainClass); |
| if ("true".equals(getAggregate())) { |
| mojo.setDotAggregationEnabled(true); |
| } |
| mojo.setOutputDirectory(outputDirectory.getAbsolutePath()); |
| mojo.setDuration(duration); |
| mojo.setLog(getLog()); |
| mojo.setPluginContext(getPluginContext()); |
| mojo.setApplicationContextUri(applicationContextUri); |
| mojo.setFileApplicationContextUri(fileApplicationContextUri); |
| try { |
| mojo.executeWithoutWrapping(); |
| } catch (Exception e) { |
| getLog().error("Failed to run Camel embedded: " + e, e); |
| } |
| } |
| } |
| |
| protected void writeIndexHtmlFile(File dir, String fileName, String content) throws IOException { |
| // File dir = outputDirectory; |
| dir.mkdirs(); |
| File html = new File(dir, fileName); |
| PrintWriter out = null; |
| try { |
| out = new PrintWriter(new FileWriter(html)); |
| out.println("<html>"); |
| out.println("<head>"); |
| out.println("</head>"); |
| out.println("<body>"); |
| out.println(); |
| if (content == null) { |
| out.write("<p>No EIP diagrams available</p>"); |
| } else { |
| out.write(content); |
| } |
| out.println("</body>"); |
| out.println("</html>"); |
| } finally { |
| String description = "Failed to close html output file"; |
| close(out, description); |
| } |
| } |
| |
| protected void printHtmlHeader(PrintWriter out, String contextName) { |
| if (contextName != null) { |
| out.println("<h1>EIP Patterns for CamelContext: " + contextName + "</h1>"); |
| } else { |
| out.println("<h1>Camel EIP Patterns</h1>"); |
| } |
| out.println(); |
| } |
| |
| protected void printHtmlFileHeader(PrintWriter out, File file) { |
| out.println("<p>"); |
| out.println(" <img src='" + removeFileExtension(file.getName()) + ".png' usemap='#CamelRoutes'>"); |
| } |
| |
| protected void printHtmlFileFooter(PrintWriter out, File file) { |
| out.println(" </img>"); |
| out.println("</p>"); |
| out.println(); |
| } |
| |
| protected void printHtmlFooter(PrintWriter out) { |
| out.println(); |
| } |
| |
| protected void close(Closeable closeable, String description) { |
| if (closeable != null) { |
| try { |
| closeable.close(); |
| } catch (IOException e) { |
| getLog().warn(description + ": " + e); |
| } |
| } |
| } |
| |
| protected String convertFile(File file, String format) throws CommandLineException { |
| Log log = getLog(); |
| if (!useDot) { |
| log.info("DOT generation disabled."); |
| return null; |
| } else { |
| if (dotHelpExitCode() != 0) { |
| log.info("'dot -?' execution failed so DOT generation disabled."); |
| return null; |
| } |
| } |
| if (this.executable == null || this.executable.length() == 0) { |
| log.warn("Parameter <executable/> was not set in the pom.xml. Skipping conversion."); |
| return null; |
| } |
| |
| String generatedFileName = removeFileExtension(file.getAbsolutePath()) + "." + format; |
| Commandline cl = new Commandline(); |
| cl.setExecutable(executable); |
| cl.createArgument().setValue("-T" + format); |
| cl.createArgument().setValue("-o"); |
| cl.createArgument().setValue(generatedFileName); |
| cl.createArgument().setValue(file.getAbsolutePath()); |
| |
| log.debug("executing: " + cl.toString()); |
| |
| CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer(); |
| CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer(); |
| |
| int exitCode = CommandLineUtils.executeCommandLine(cl, stdout, stderr); |
| |
| String output = stdout.getOutput(); |
| if (output.length() > 0) { |
| log.debug(output); |
| } |
| String errOutput = stderr.getOutput(); |
| if (errOutput.length() > 0) { |
| log.warn(errOutput); |
| } |
| return generatedFileName; |
| } |
| |
| private int dotHelpExitCode() throws CommandLineException { |
| Commandline cl = new Commandline(); |
| cl.setExecutable(executable); |
| cl.createArgument().setValue("-?"); |
| |
| CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer(); |
| CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer(); |
| |
| return CommandLineUtils.executeCommandLine(cl, stdout, stderr); |
| } |
| |
| protected String removeFileExtension(String name) { |
| int idx = name.lastIndexOf("."); |
| if (idx > 0) { |
| return name.substring(0, idx); |
| } else { |
| return name; |
| } |
| } |
| |
| private void appendFiles(List<File> output, File file) { |
| if (file.isDirectory()) { |
| appendDirectory(output, file); |
| } else { |
| if (isValid(file)) { |
| output.add(file); |
| } |
| } |
| } |
| |
| private void appendDirectory(List<File> output, File dir) { |
| File[] files = dir.listFiles(); |
| for (File file : files) { |
| appendFiles(output, file); |
| } |
| } |
| |
| private boolean isValid(File file) { |
| String name = file.getName().toLowerCase(); |
| return name.endsWith(".dot"); |
| } |
| |
| private void addFileToBuffer(PrintWriter out, File file) throws MojoExecutionException { |
| BufferedReader reader = null; |
| try { |
| reader = new BufferedReader(new FileReader(file)); |
| while (true) { |
| String line = reader.readLine(); |
| if (line == null) { |
| break; |
| } else { |
| out.println(line); |
| } |
| } |
| } catch (IOException e) { |
| throw new MojoExecutionException("Failed: " + e, e); |
| } finally { |
| close(reader, "cmapx file"); |
| } |
| } |
| |
| /** |
| * Gets resource bundle for given locale. |
| * |
| * @param locale locale |
| * @return resource bundle |
| */ |
| protected ResourceBundle getBundle(final Locale locale) { |
| return ResourceBundle.getBundle("camel-maven-plugin", locale, this.getClass().getClassLoader()); |
| } |
| |
| protected Renderer getSiteRenderer() { |
| return this.renderer; |
| } |
| |
| protected String getOutputDirectory() { |
| return this.outputDirectory.getAbsolutePath(); |
| } |
| |
| protected MavenProject getProject() { |
| return this.project; |
| } |
| } |