blob: 347ee1f4badb5d4ca7f0e51e3a5a28b128f736f5 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2015 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("&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.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);
}
}
}