You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2023/01/28 19:20:06 UTC

[tapestry-5] 02/02: TAP5-2744: finished Graphviz component and PageDependencyGraph page

This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch better-page-invalidation
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit 14e2243ca5e4fa65e7c391e09564cb1aafb57fb7
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Sat Jan 28 16:19:21 2023 -0300

    TAP5-2744: finished Graphviz component and PageDependencyGraph page
---
 .../META-INF/modules/t5/core/graphviz.coffee       |  29 ++++++
 .../tapestry5/corelib/components/Graphviz.java     | 104 +++++++++++++++++++++
 .../tapestry5/corelib/pages/PageCatalog.java       |  18 +---
 .../corelib/pages/PageDependencyGraph.java         |  71 ++++++++++++++
 .../ComponentDependencyGraphvizGeneratorImpl.java  |   4 +-
 .../apache/tapestry5/modules/DashboardModule.java  |   6 +-
 .../META-INF/assets/tapestry5/PageCatalog.js       |   9 --
 .../resources/org/apache/tapestry5/core.properties |   3 +
 .../apache/tapestry5/corelib/pages/PageCatalog.tml |   4 +-
 .../corelib/pages/PageDependencyGraph.tml          |  13 +++
 10 files changed, 231 insertions(+), 30 deletions(-)

diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/graphviz.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/graphviz.coffee
new file mode 100644
index 000000000..e1fbe3ee2
--- /dev/null
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/graphviz.coffee
@@ -0,0 +1,29 @@
+# Copyright 2023 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ## t5/core/graphviz
+#
+# Support to the core/Graphviz Tapestry component.
+define ["https://cdn.jsdelivr.net/npm/@hpcc-js/wasm/dist/graphviz.umd.js"],
+  (hpccWasm) ->
+    render = (value, id, showDownloadLink) ->
+      hpccWasm.Graphviz.load().then (graphviz) ->
+        svg = graphviz.dot value
+        div = document.getElementById id
+        layout = graphviz.layout(value, "svg", "dot")
+        div.innerHTML = layout
+        if showDownloadLink
+          link = document.getElementById (id + "-download")
+          link.setAttribute "href", "data:image/svg+xml;charset=utf-8," + encodeURIComponent(layout)
+    return render
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Graphviz.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Graphviz.java
new file mode 100644
index 000000000..8fd74f4c8
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Graphviz.java
@@ -0,0 +1,104 @@
+// Copyright 2023 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.corelib.components;
+
+import org.apache.tapestry5.BindingConstants;
+import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.annotations.Environmental;
+import org.apache.tapestry5.annotations.Parameter;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.commons.Messages;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
+import org.apache.tapestry5.services.javascript.JavaScriptSupport;
+
+/**
+ * Component that renders a <a href="http://graphviz.org">Graphviz</a> graph using
+ * <a href="https://www.npmjs.com/package/@hpcc-js/wasm">@hpcc-js/wasm</a>. It's mostly 
+ * intended to be used internally at Tapestry, hence the lack of options.
+ * 
+ * @tapestrydoc
+ */
+public class Graphviz
+{
+    
+    /**
+     * A Graphviz graph described in its DOT language.
+     */
+    @Parameter(required = true, allowNull = false)
+    @Property
+    private String value;
+    
+    /**
+     * Defines whether a link to download the graph as an SVG file should be provided.
+     */
+    @Parameter(defaultPrefix = BindingConstants.LITERAL, value = "false")
+    private boolean showDownloadLink;
+
+    /**
+     * Defines whether a the Graphviz source should be shown.
+     */
+    @Parameter(defaultPrefix = BindingConstants.LITERAL, value = "false")
+    private boolean showSource;
+
+    @Environmental
+    private JavaScriptSupport javaScriptSupport;
+    
+    @Inject
+    private AjaxResponseRenderer ajaxResponseRenderer;
+    
+    @Inject
+    private ComponentResources resources;
+    
+    @Inject
+    private Messages messages;
+
+    // Read value only once if showSource = true
+    private String cachedValue;
+    
+    void setupRender(MarkupWriter writer)
+    {
+     
+        cachedValue = value;
+        String elementName = resources.getElementName();
+        if (elementName == null)
+        {
+            elementName = "div";
+        }
+        
+        final String id = javaScriptSupport.allocateClientId(resources);
+        writer.element(elementName, "id", id);
+        writer.end();
+        
+        javaScriptSupport.require("t5/core/graphviz").with(cachedValue, id, showDownloadLink);
+        
+        if (showDownloadLink)
+        {
+            writer.element("a", "href", "#", "id", id + "-download", "download", id + ".svg");
+            writer.write(messages.get("download-graphviz-image"));
+            writer.end();
+        }
+        
+        if (showSource)
+        {
+            writer.element("pre", "id", id + "-source");
+            writer.write(cachedValue);
+            writer.end();
+        }
+        
+    }
+    
+}
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java
index 0728b6fe2..74479881d 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java
@@ -21,11 +21,9 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
-import org.apache.tapestry5.Asset;
 import org.apache.tapestry5.MarkupWriter;
 import org.apache.tapestry5.alerts.AlertManager;
 import org.apache.tapestry5.annotations.InjectComponent;
-import org.apache.tapestry5.annotations.Path;
 import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;
 import org.apache.tapestry5.annotations.UnknownActivationContextCheck;
@@ -152,10 +150,6 @@ public class PageCatalog
     @Inject
     private AjaxResponseRenderer ajaxResponseRenderer;
     
-    @Inject
-    @Path("classpath:/META-INF/assets/tapestry5/PageCatalog.js")
-    private Asset pageCatalogJs;
-
     void pageLoaded()
     {
         model = beanModelSource.createDisplayModel(Page.class, messages);
@@ -372,14 +366,6 @@ public class PageCatalog
     {
         selectedPage = pageSource.getPage(pageName);
         ajaxResponseRenderer.addRender("pageStructureZone", pageStructureZone.getBody());
-        ajaxResponseRenderer.addCallback((JavaScriptSupport js) -> {
-            js.importJavaScriptLibrary(pageCatalogJs);
-            js.importJavaScriptLibrary("https://cdn.jsdelivr.net/npm/@hpcc-js/wasm/dist/graphviz.umd.js");
-            final String graphvizSource = getGraphvizSource(getSelectedPageClassName());
-            System.out.println(graphvizSource);
-            js.addScript("showGraphviz('%s', '%s');", pageName, 
-                    graphvizSource.replace("\n", " "));
-        });
     }
     
     public String getDisplayLogicalName() 
@@ -462,9 +448,9 @@ public class PageCatalog
         return resolver.getLogicalName(className);
     }
     
-    private String getGraphvizSource(String className)
+    public String getGraphvizValue()
     {
-        return componentDependencyGraphvizGenerator.generate(className);
+        return componentDependencyGraphvizGenerator.generate(getClassName(selectedPage));
     }
     
 }
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageDependencyGraph.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageDependencyGraph.java
new file mode 100644
index 000000000..134b87e2c
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageDependencyGraph.java
@@ -0,0 +1,71 @@
+// Copyright 2023 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.corelib.pages;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.annotations.UnknownActivationContextCheck;
+import org.apache.tapestry5.annotations.WhitelistAccessOnly;
+import org.apache.tapestry5.internal.services.ComponentDependencyGraphvizGenerator;
+import org.apache.tapestry5.internal.services.PageSource;
+import org.apache.tapestry5.internal.structure.Page;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.services.ComponentClassResolver;
+import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
+
+/**
+ * Shows graph showing the dependencies of all already loaded pages and its compnoents, mixins and base classes.
+ */
+@UnknownActivationContextCheck(false)
+@WhitelistAccessOnly
+public class PageDependencyGraph
+{
+
+    @Inject
+    private ComponentClassResolver resolver;
+    
+    @Inject
+    private ComponentDependencyGraphvizGenerator componentDependencyGraphvizGenerator;
+    
+    @Inject
+    private PageSource pageSource;
+
+    @Inject
+    private ComponentClassResolver componentClassResolver;
+
+    @Inject
+    private AjaxResponseRenderer ajaxResponseRenderer;
+    
+    @Property
+    private Page page;
+    
+    private String getClassName(Page page) 
+    {
+        return page.getRootComponent().getComponentResources().getComponentModel().getComponentClassName();
+    }
+
+    public String getGraphvizValue()
+    {
+        final Set<Page> allPages = pageSource.getAllPages();
+        return componentDependencyGraphvizGenerator.generate(
+                allPages.stream()
+                    .map(this::getClassName)
+                    .collect(Collectors.toList())
+                    .toArray(new String[allPages.size()]));
+    }
+    
+}
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyGraphvizGeneratorImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyGraphvizGeneratorImpl.java
index f53816687..81a179e46 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyGraphvizGeneratorImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyGraphvizGeneratorImpl.java
@@ -42,8 +42,8 @@ public class ComponentDependencyGraphvizGeneratorImpl implements ComponentDepend
 
         dotFile.append("\trankdir=LR;\n");
         dotFile.append("\tfontname=\"Helvetica,Arial,sans-serif\";\n");
-//      dotFile.append("\tnode [fontname=\"Helvetica,Arial,sans-serif\",fontsize=\"8pt\"];\n");
-        dotFile.append("\tedge [fontname=\"Helvetica,Arial,sans-serif\"];\n");
+        dotFile.append("\tsplines=ortho;\n\n");
+        dotFile.append("\tnode [fontname=\"Helvetica,Arial,sans-serif\",fontsize=\"10pt\"];\n");
         dotFile.append("\tnode [shape=rect];\n\n");
         
         final Set<String> allClasses = new HashSet<>();
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/DashboardModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/DashboardModule.java
index 002fea71d..c4985b510 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/DashboardModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/DashboardModule.java
@@ -1,4 +1,4 @@
-// Copyright 2013, 2014 The Apache Software Foundation
+// Copyright 2013, 2014, 2023 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
 package org.apache.tapestry5.modules;
 
 import org.apache.tapestry5.commons.OrderedConfiguration;
+import org.apache.tapestry5.internal.services.ComponentDependencyGraphvizGenerator;
+import org.apache.tapestry5.internal.services.ComponentDependencyGraphvizGeneratorImpl;
 import org.apache.tapestry5.internal.services.dashboard.DashboardManagerImpl;
 import org.apache.tapestry5.ioc.ServiceBinder;
 import org.apache.tapestry5.ioc.annotations.Contribute;
@@ -26,6 +28,7 @@ public class DashboardModule
     public static void bind(ServiceBinder binder)
     {
         binder.bind(DashboardManager.class, DashboardManagerImpl.class);
+        binder.bind(ComponentDependencyGraphvizGenerator.class, ComponentDependencyGraphvizGeneratorImpl.class);
     }
 
     @Contribute(DashboardManager.class)
@@ -34,5 +37,6 @@ public class DashboardModule
         configuration.add("Pages", new DashboardTab("Pages", "core/PageCatalog"));
         configuration.add("Services", new DashboardTab("Services", "core/ServiceStatus"));
         configuration.add("Libraries", new DashboardTab("ComponentLibraries", "core/ComponentLibraries"));
+        configuration.add("PageDependencyGraph", new DashboardTab("PageDependencyGraph", "core/PageDependencyGraph"));
     }
 }
diff --git a/tapestry-core/src/main/resources/META-INF/assets/tapestry5/PageCatalog.js b/tapestry-core/src/main/resources/META-INF/assets/tapestry5/PageCatalog.js
deleted file mode 100644
index c3be055a5..000000000
--- a/tapestry-core/src/main/resources/META-INF/assets/tapestry5/PageCatalog.js
+++ /dev/null
@@ -1,9 +0,0 @@
-console.log("PageCatalog.js");
-function showGraphviz(pageName, dot) {
-	var hpccWasm = require("https://cdn.jsdelivr.net/npm/@hpcc-js/wasm/dist/graphviz.umd.js");
-    hpccWasm.Graphviz.load().then(graphviz => {	        	
-		const svg = graphviz.dot(dot);
-		const div = document.getElementById(pageName + "-graphviz");
-		div.innerHTML = graphviz.layout(dot, "svg", "dot");
-	});
-};
\ No newline at end of file
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties b/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties
index 61cac585b..bda821d34 100644
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/core.properties
@@ -140,6 +140,9 @@ private-default-localdate-format=lll
 # see ComponentLibraries page
 not-informed=Not informed
 
+# see Graphviz
+download-graphviz-image = Download graph as SVG
+
 # OpenAPI generation
 openapi.viewer-title=OpenAPI definition viewer
 openapi-title=OpenAPI description
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.tml
index 688f8bd05..cc7ae7056 100644
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.tml
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.tml
@@ -67,8 +67,8 @@
 	    </div>
 	    <div class="panel panel-default vert-offset" t:type="If" t:test="selectedPage">
 	        <div class="panel-heading">${selectedPage.name}'s dependency tree</div>
-<!-- 	        <pre id="${selectedPage.name}-graphviz-source">${getGraphVizSource(selectedPageClassName)}</pre> -->
-	        <div class="panel-body" id="${selectedPage.name}-graphviz">
+	        <div class="panel-body">
+	        	<t:graphviz value="graphvizValue"/>
 	        </div>
 	    </div>
 	</t:zone>	   
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageDependencyGraph.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageDependencyGraph.tml
new file mode 100644
index 000000000..21a6ec7c1
--- /dev/null
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageDependencyGraph.tml
@@ -0,0 +1,13 @@
+<t:block id="content" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
+         xmlns:p="tapestry:parameter">
+
+	<p>
+		This page provides a graph, generated with <a href="http://graphviz.org">Graphviz</a>
+		and <a href="https://www.npmjs.com/package/@hpcc-js/wasm">@hpcc-js/wasm</a>,
+		showing the dependencies of all already loaded
+		pages and its components, mixins and base classes.
+	</p>
+	
+	<t:Graphviz value="graphvizValue" showDownloadLink="true" showSource="true"/>
+
+</t:block>
\ No newline at end of file