Adding --write-module-graph=<filename>
This commit is contained in:
parent
8fdc6b05bb
commit
444a49f97e
|
@ -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())
|
||||
|
|
|
@ -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>
|
||||
{
|
||||
|
|
|
@ -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("<"); 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.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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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"))
|
||||
{
|
||||
|
|
|
@ -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:
|
||||
--------------------------------
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue