View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.start;
20  
21  import java.io.BufferedWriter;
22  import java.io.IOException;
23  import java.io.PrintWriter;
24  import java.nio.charset.StandardCharsets;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.StandardOpenOption;
28  import java.util.Collection;
29  import java.util.List;
30  
31  import org.eclipse.jetty.start.graph.Graph;
32  import org.eclipse.jetty.start.graph.Node;
33  import org.eclipse.jetty.start.graph.Selection;
34  
35  /**
36   * Generate a graphviz dot graph of the modules found
37   */
38  public class ModuleGraphWriter
39  {
40      private String colorModuleBg;
41      private String colorEnabledBg;
42      private String colorTransitiveBg;
43      private String colorCellBg;
44      private String colorHeaderBg;
45      private String colorModuleFont;
46  
47      public ModuleGraphWriter()
48      {
49          colorModuleBg = "#B8FFB8";
50          colorEnabledBg = "#66FFCC";
51          colorTransitiveBg = "#66CC66";
52          colorCellBg = "#FFFFFF80";
53          colorHeaderBg = "#00000020";
54          colorModuleFont = "#888888";
55      }
56  
57      public void config(Props props)
58      {
59          String prefix = "jetty.graph.";
60          colorModuleBg = getProperty(props,prefix + "color.module.bg",colorModuleBg);
61          colorEnabledBg = getProperty(props,prefix + "color.enabled.bg",colorEnabledBg);
62          colorTransitiveBg = getProperty(props,prefix + "color.transitive.bg",colorTransitiveBg);
63          colorCellBg = getProperty(props,prefix + "color.cell.bg",colorCellBg);
64          colorHeaderBg = getProperty(props,prefix + "color.header.bg",colorHeaderBg);
65          colorModuleFont = getProperty(props,prefix + "color.font",colorModuleFont);
66      }
67  
68      private String getProperty(Props props, String key, String defVal)
69      {
70          String val = props.getString(key,defVal);
71          if (val == null)
72          {
73              return defVal;
74          }
75          val = val.trim();
76          if (val.length() <= 0)
77          {
78              return defVal;
79          }
80          return val;
81      }
82  
83      public void write(Modules modules, Path outputFile) throws IOException
84      {
85          try (BufferedWriter writer = Files.newBufferedWriter(outputFile,StandardCharsets.UTF_8,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE); 
86               PrintWriter out = new PrintWriter(writer);)
87          {
88              writeHeaderMessage(out,outputFile);
89  
90              out.println();
91              out.println("digraph modules {");
92  
93              // Node Style
94              out.println("  node [color=gray, style=filled, shape=rectangle];");
95              out.println("  node [fontname=\"Verdana\", size=\"20,20\"];");
96              // Graph Style
97              out.println("  graph [");
98              out.println("    concentrate=false,");
99              out.println("    fontname=\"Verdana\",");
100             out.println("    fontsize = 20,");
101             out.println("    rankdir = LR,");
102             out.println("    ranksep = 1.5,");
103             out.println("    nodesep = .5,");
104             out.println("    style = bold,");
105             out.println("    labeljust = l,");
106             out.println("    label = \"Jetty Modules\",");
107             out.println("    ssize = \"20,40\"");
108             out.println("  ];");
109 
110             List<Module> enabled = modules.getSelected();
111 
112             // Module Nodes
113             writeModules(out,modules,enabled);
114 
115             // Module Relationships
116             writeRelationships(out,modules,enabled);
117 
118             out.println("}");
119             out.println();
120         }
121     }
122 
123     private void writeHeaderMessage(PrintWriter out, Path outputFile)
124     {
125         out.println("/*");
126         out.println(" * GraphViz Graph of Jetty Modules");
127         out.println(" * ");
128         out.println(" * Jetty: http://eclipse.org/jetty/");
129         out.println(" * GraphViz: http://graphviz.org/");
130         out.println(" * ");
131         out.println(" * To Generate Graph image using graphviz:");
132         String filename = outputFile.getFileName().toString();
133         String basename = filename.substring(0,filename.indexOf('.'));
134         out.printf(" *   $ dot -Tpng -Goverlap=false -o %s.png %s%n",basename,filename);
135         out.println(" */");
136     }
137 
138     private void writeModuleDetailHeader(PrintWriter out, String header)
139     {
140         writeModuleDetailHeader(out,header,1);
141     }
142 
143     private void writeModuleDetailHeader(PrintWriter out, String header, int count)
144     {
145         out.printf("  <TR>");
146         out.printf("<TD BGCOLOR=\"%s\" ALIGN=\"LEFT\"><I>",colorHeaderBg);
147         out.printf("%s%s</I></TD>",header,count > 1?"s":"");
148         out.println("</TR>");
149     }
150 
151     private void writeModuleDetailLine(PrintWriter out, String line)
152     {
153         out.printf("  <TR>");
154         StringBuilder escape = new StringBuilder();
155         for(char c: line.toCharArray()) {
156             switch(c) {
157                 case '<': escape.append("&lt;"); break;
158                 case '>': escape.append("&gt;"); break;
159                 default:
160                     escape.append(c);
161                     break;
162             }
163         }
164         
165         out.printf("<TD BGCOLOR=\"%s\" ALIGN=\"LEFT\">%s</TD></TR>%n",colorCellBg,escape.toString());
166     }
167 
168     private void writeModuleNode(PrintWriter out, Module module, boolean resolved)
169     {
170         String color = colorModuleBg;
171         if (module.isSelected())
172         {
173             // specifically enabled by config
174             color = colorEnabledBg;
175         }
176         else if (resolved)
177         {
178             // enabled by transitive reasons
179             color = colorTransitiveBg;
180         }
181 
182         out.printf("  \"%s\" [ color=\"%s\" label=<",module.getName(),color);
183         out.printf("<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"2\">%n");
184         out.printf("  <TR><TD ALIGN=\"LEFT\"><B>%s</B></TD></TR>%n",module.getName());
185 
186         if (module.isSelected())
187         {
188             writeModuleDetailHeader(out,"ENABLED");
189             for (Selection selection : module.getSelections())
190             {
191                 writeModuleDetailLine(out,"via: " + selection);
192             }
193         }
194         else if (resolved)
195         {
196             writeModuleDetailHeader(out,"TRANSITIVE");
197         }
198 
199         if (!module.getXmls().isEmpty())
200         {
201             List<String> xmls = module.getXmls();
202             writeModuleDetailHeader(out,"XML",xmls.size());
203             for (String xml : xmls)
204             {
205                 writeModuleDetailLine(out,xml);
206             }
207         }
208 
209         if (!module.getLibs().isEmpty())
210         {
211             List<String> libs = module.getLibs();
212             writeModuleDetailHeader(out,"LIB",libs.size());
213             for (String lib : libs)
214             {
215                 writeModuleDetailLine(out,lib);
216             }
217         }
218 
219         if (!module.getIniTemplate().isEmpty())
220         {
221             List<String> inis = module.getIniTemplate();
222             writeModuleDetailHeader(out,"INI Template",inis.size());
223         }
224 
225         out.printf("</TABLE>>];%n");
226     }
227 
228     private void writeModules(PrintWriter out, Modules allmodules, List<Module> enabled)
229     {
230         out.println();
231         out.println("  /* Modules */");
232         out.println();
233 
234         out.println("  node [ labeljust = l ];");
235 
236         for (int depth = 0; depth <= allmodules.getMaxDepth(); depth++)
237         {
238             out.println();
239             Collection<Module> depthModules = allmodules.getModulesAtDepth(depth);
240             if (depthModules.size() > 0)
241             {
242                 out.printf("  /* Level %d */%n",depth);
243                 out.println("  { rank = same;");
244                 for (Module module : depthModules)
245                 {
246                     boolean resolved = enabled.contains(module);
247                     writeModuleNode(out,module,resolved);
248                 }
249                 out.println("  }");
250             }
251         }
252     }
253 
254     private void writeRelationships(PrintWriter out, Graph<Module> modules, List<Module> enabled)
255     {
256         for (Module module : modules)
257         {
258             for (Node<?> parent : module.getParentEdges())
259             {
260                 out.printf("    \"%s\" -> \"%s\";%n",module.getName(),parent.getName());
261             }
262         }
263     }
264 }