| // |
| // ======================================================================== |
| // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.start; |
| |
| import java.io.BufferedWriter; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.StandardOpenOption; |
| import java.util.List; |
| |
| /** |
| * Generate a graphviz dot graph of the modules found |
| */ |
| public class ModuleGraphWriter |
| { |
| private String colorModuleBg; |
| private String colorEnabledBg; |
| private String colorTransitiveBg; |
| private String colorCellBg; |
| private String colorHeaderBg; |
| private String colorModuleFont; |
| |
| public ModuleGraphWriter() |
| { |
| colorModuleBg = "#B8FFB8"; |
| colorEnabledBg = "#66FFCC"; |
| colorTransitiveBg = "#66CC66"; |
| colorCellBg = "#FFFFFF80"; |
| colorHeaderBg = "#00000020"; |
| colorModuleFont = "#888888"; |
| } |
| |
| public void config(Props props) |
| { |
| String prefix = "jetty.graph."; |
| colorModuleBg = getProperty(props,prefix + "color.module.bg",colorModuleBg); |
| colorEnabledBg = getProperty(props,prefix + "color.enabled.bg",colorEnabledBg); |
| colorTransitiveBg = getProperty(props,prefix + "color.transitive.bg",colorTransitiveBg); |
| colorCellBg = getProperty(props,prefix + "color.cell.bg",colorCellBg); |
| colorHeaderBg = getProperty(props,prefix + "color.header.bg",colorHeaderBg); |
| colorModuleFont = getProperty(props,prefix + "color.font",colorModuleFont); |
| } |
| |
| private String getProperty(Props props, String key, String defVal) |
| { |
| String val = props.getString(key,defVal); |
| if (val == null) |
| { |
| return defVal; |
| } |
| val = val.trim(); |
| if (val.length() <= 0) |
| { |
| return defVal; |
| } |
| return val; |
| } |
| |
| public void write(Modules modules, Path outputFile) throws IOException |
| { |
| try (BufferedWriter writer = Files.newBufferedWriter(outputFile,StandardCharsets.UTF_8,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE); |
| PrintWriter out = new PrintWriter(writer);) |
| { |
| writeHeaderMessage(out,outputFile); |
| |
| out.println(); |
| out.println("digraph modules {"); |
| |
| // Node Style |
| out.println(" node [color=gray, style=filled, shape=rectangle];"); |
| out.println(" node [fontname=\"Verdana\", size=\"20,20\"];"); |
| // Graph Style |
| out.println(" graph ["); |
| out.println(" concentrate=false,"); |
| out.println(" fontname=\"Verdana\","); |
| out.println(" fontsize = 20,"); |
| out.println(" rankdir = LR,"); |
| out.println(" ranksep = 1.5,"); |
| out.println(" nodesep = .5,"); |
| out.println(" style = bold,"); |
| out.println(" labeljust = l,"); |
| out.println(" label = \"Jetty Modules\","); |
| out.println(" ssize = \"20,40\""); |
| out.println(" ];"); |
| |
| List<Module> enabled = modules.getSelected(); |
| |
| // Module Nodes |
| writeModules(out,modules,enabled); |
| |
| // Module Relationships |
| writeRelationships(out,modules,enabled); |
| |
| out.println("}"); |
| out.println(); |
| } |
| } |
| |
| private void writeHeaderMessage(PrintWriter out, Path outputFile) |
| { |
| out.println("/*"); |
| out.println(" * GraphViz Graph of Jetty Modules"); |
| out.println(" * "); |
| out.println(" * Jetty: http://eclipse.org/jetty/"); |
| out.println(" * GraphViz: http://graphviz.org/"); |
| out.println(" * "); |
| out.println(" * To Generate Graph image using graphviz:"); |
| String filename = outputFile.getFileName().toString(); |
| String basename = filename.substring(0,filename.indexOf('.')); |
| out.printf(" * $ dot -Tpng -Goverlap=false -o %s.png %s%n",basename,filename); |
| out.println(" */"); |
| } |
| |
| private void writeModuleDetailHeader(PrintWriter out, String header) |
| { |
| writeModuleDetailHeader(out,header,1); |
| } |
| |
| private void writeModuleDetailHeader(PrintWriter out, String header, int count) |
| { |
| out.printf(" <TR>"); |
| out.printf("<TD BGCOLOR=\"%s\" ALIGN=\"LEFT\"><I>",colorHeaderBg); |
| out.printf("%s%s</I></TD>",header,count > 1?"s":""); |
| out.println("</TR>"); |
| } |
| |
| private void writeModuleDetailLine(PrintWriter out, String line) |
| { |
| out.printf(" <TR>"); |
| StringBuilder escape = new StringBuilder(); |
| for(char c: line.toCharArray()) { |
| switch(c) { |
| case '<': escape.append("<"); break; |
| case '>': escape.append(">"); break; |
| default: |
| escape.append(c); |
| break; |
| } |
| } |
| |
| out.printf("<TD BGCOLOR=\"%s\" ALIGN=\"LEFT\">%s</TD></TR>%n",colorCellBg,escape.toString()); |
| } |
| |
| private void writeModuleNode(PrintWriter out, Module module, boolean resolved) |
| { |
| String color = colorModuleBg; |
| if (module.isSelected()) |
| { |
| // specifically enabled by config |
| color = colorEnabledBg; |
| } |
| else if (resolved) |
| { |
| // enabled by transitive reasons |
| color = colorTransitiveBg; |
| } |
| |
| out.printf(" \"%s\" [ color=\"%s\" label=<",module.getName(),color); |
| out.printf("<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"2\">%n"); |
| out.printf(" <TR><TD ALIGN=\"LEFT\"><B>%s</B></TD></TR>%n",module.getName()); |
| |
| if (module.isSelected()) |
| { |
| writeModuleDetailHeader(out,"ENABLED"); |
| for (String selection : module.getSelections()) |
| { |
| writeModuleDetailLine(out,"via: " + selection); |
| } |
| } |
| else if (resolved) |
| { |
| writeModuleDetailHeader(out,"TRANSITIVE"); |
| } |
| |
| if (!module.getXmls().isEmpty()) |
| { |
| List<String> xmls = module.getXmls(); |
| writeModuleDetailHeader(out,"XML",xmls.size()); |
| for (String xml : xmls) |
| { |
| writeModuleDetailLine(out,xml); |
| } |
| } |
| |
| if (!module.getLibs().isEmpty()) |
| { |
| List<String> libs = module.getLibs(); |
| writeModuleDetailHeader(out,"LIB",libs.size()); |
| for (String lib : libs) |
| { |
| writeModuleDetailLine(out,lib); |
| } |
| } |
| |
| if (!module.getIniTemplate().isEmpty()) |
| { |
| List<String> inis = module.getIniTemplate(); |
| writeModuleDetailHeader(out,"INI Template",inis.size()); |
| } |
| |
| out.printf("</TABLE>>];%n"); |
| } |
| |
| private void writeModules(PrintWriter out, Modules allmodules, List<Module> enabled) |
| { |
| out.println(); |
| out.println(" /* Modules */"); |
| out.println(); |
| |
| out.println(" node [ labeljust = l ];"); |
| |
| for (Module module: allmodules) |
| { |
| boolean resolved = enabled.contains(module); |
| writeModuleNode(out,module,resolved); |
| } |
| } |
| |
| private void writeRelationships(PrintWriter out, Iterable<Module> modules, List<Module> enabled) |
| { |
| for (Module module : modules) |
| { |
| for (String depends : module.getDepends()) |
| out.printf(" \"%s\" -> \"%s\";%n",module.getName(),depends); |
| for (String optional : module.getOptional()) |
| out.printf(" \"%s\" => \"%s\";%n",module.getName(),optional); |
| } |
| } |
| } |