Adding --write-module-graph=<filename>

This commit is contained in:
Joakim Erdfelt 2013-08-30 13:08:32 -07:00
parent 8fdc6b05bb
commit 444a49f97e
7 changed files with 369 additions and 5 deletions

View File

@ -611,6 +611,16 @@ public class Main
{
listModules(args);
}
// Generate Module Graph File
if (args.getModuleGraphFilename() != null)
{
File outputFile = baseHome.getBaseFile(args.getModuleGraphFilename());
System.out.printf("Generating GraphViz Graph of Jetty Modules at %s%n",baseHome.toShortForm(outputFile));
ModuleGraphWriter writer = new ModuleGraphWriter();
writer.config(args.getProperties());
writer.write(args.getAllModules(),outputFile);
}
// Show Command Line to execute Jetty
if (args.isDryRun())

View File

@ -30,17 +30,14 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.omg.CORBA.INITIALIZE;
/**
* Represents a Module metadata, as defined in Jetty.
*/
public class Module // extends TextFile
public class Module
{
public static class NameComparator implements Comparator<Module>
{

View File

@ -0,0 +1,257 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
/**
* 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(Properties 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(Properties props, String key, String defVal)
{
String val = props.getProperty(key,defVal);
if (val == null)
{
return defVal;
}
val = val.trim();
if (val.length() <= 0)
{
return defVal;
}
return val;
}
public void write(Modules modules, File outputFile) throws IOException
{
try (FileWriter writer = new FileWriter(outputFile,false); 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.resolveEnabled();
// Module Nodes
writeModules(out,modules,enabled);
// Module Relationships
writeRelationships(out,modules,enabled);
out.println("}");
out.println();
}
}
private void writeHeaderMessage(PrintWriter out, File 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.getName();
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("&lt;"); break;
case '>': escape.append("&gt;"); 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.isEnabled())
{
// 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.isEnabled())
{
writeModuleDetailHeader(out,"ENABLED");
for (String source : module.getSources())
{
writeModuleDetailLine(out,"via: " + source);
}
}
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.getInitialise().isEmpty())
{
List<String> inis = module.getInitialise();
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 (int depth = 0; depth <= allmodules.getMaxDepth(); depth++)
{
out.println();
Collection<Module> depthModules = allmodules.getModulesAtDepth(depth);
if (depthModules.size() > 0)
{
out.printf(" /* Level %d */%n",depth);
out.println(" { rank = same;");
for (Module module : depthModules)
{
boolean resolved = enabled.contains(module);
writeModuleNode(out,module,resolved);
}
out.println(" }");
}
}
}
private void writeRelationships(PrintWriter out, Modules modules, List<Module> enabled)
{
for (Module module : modules)
{
for (Module parent : module.getParentEdges())
{
out.printf(" \"%s\" -> \"%s\";%n",module.getName(),parent.getName());
}
}
}
}

View File

@ -37,6 +37,7 @@ import java.util.Stack;
public class Modules implements Iterable<Module>
{
private Map<String, Module> modules = new HashMap<>();
private int maxDepth = -1;
private Set<String> asNameSet(Set<Module> moduleSet)
{
@ -83,6 +84,7 @@ public class Modules implements Iterable<Module>
for (Module child : module.getChildEdges())
{
child.setDepth(Math.max(depth,child.getDepth()));
this.maxDepth = Math.max(this.maxDepth,child.getDepth());
}
// Dive down
@ -118,7 +120,7 @@ public class Modules implements Iterable<Module>
for (String optionalParentName : module.getOptionalParentNames())
{
Module optional = get(optionalParentName);
if (optional==null)
if (optional == null)
{
System.err.printf("WARNING: module not found [%s]%n",optionalParentName);
}
@ -242,6 +244,24 @@ public class Modules implements Iterable<Module>
return modules.get(name);
}
public int getMaxDepth()
{
return maxDepth;
}
public Set<Module> getModulesAtDepth(int depth)
{
Set<Module> ret = new HashSet<>();
for (Module module : modules.values())
{
if (module.getDepth() == depth)
{
ret.add(module);
}
}
return ret;
}
@Override
public Iterator<Module> iterator()
{

View File

@ -79,6 +79,7 @@ public class StartArgs
private List<String> jvmArgs = new ArrayList<>();
private List<String> moduleIni = new ArrayList<>();
private List<String> moduleStartIni = new ArrayList<>();
private String moduleGraphFilename;
private Modules allModules;
// Should the server be run?
@ -439,6 +440,11 @@ public class StartArgs
return System.getProperty("main.class",mainclass);
}
public String getModuleGraphFilename()
{
return moduleGraphFilename;
}
public List<String> getModuleIni()
{
return moduleIni;
@ -711,6 +717,13 @@ public class StartArgs
return;
}
if (arg.startsWith("--write-module-graph="))
{
this.moduleGraphFilename = getValue(arg);
run = false;
return;
}
// Start property (syntax similar to System property)
if (arg.startsWith("-D"))
{

View File

@ -82,6 +82,13 @@ Module Management:
enabled in the ${jetty.base}/start.ini using the same
techniques.
--write-module-graph=<filename>
Create a graphviz *.dot file of the module graph as it
exists for the active ${jetty.base}.
See http://graphviz.org/ for details on how to post-process
this file into the output best suited for your needs.
Startup / Shutdown Command Line:
--------------------------------

View File

@ -0,0 +1,60 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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 static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
public class ModuleGraphWriterTest
{
@SuppressWarnings("unused")
private final static List<String> TEST_SOURCE = Collections.singletonList("<test>");
@Rule
public TestingDir testdir = new TestingDir();
@Test
public void testGenerate_NothingEnabled() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
File baseDir = testdir.getEmptyDir();
BaseHome basehome = new BaseHome(homeDir,baseDir);
Modules modules = new Modules();
modules.registerAll(basehome);
modules.buildGraph();
File outputFile = new File(baseDir,"graph.dot");
ModuleGraphWriter writer = new ModuleGraphWriter();
writer.write(modules,outputFile);
Assert.assertThat("Output File Exists",outputFile.exists(),is(true));
}
}