You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/12/03 15:44:49 UTC
[isis] branch master updated: ISIS-2903: adds CallStack Visualization to _Xray
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 65aab3d ISIS-2903: adds CallStack Visualization to _Xray
65aab3d is described below
commit 65aab3dda13acd5b4b1d57d557249da6d0eb8da7
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Dec 3 16:43:42 2021 +0100
ISIS-2903: adds CallStack Visualization to _Xray
---
.../apache/isis/commons/internal/debug/_Debug.java | 25 ++-
.../commons/internal/debug/xray/XrayDataModel.java | 27 +---
.../isis/commons/internal/debug/xray/XrayUi.java | 76 ++++++++-
.../internal/debug/xray/_CallStackMerger.java | 178 +++++++++++++++++++++
.../commons/internal/debug/xray/_SwingUtil.java | 41 +++--
.../debug/xray/graphics/CallStackDiagram.java | 37 +++++
.../{sequence => graphics}/SequenceDiagram.java | 39 +++--
.../xray/{sequence => graphics}/_Graphics.java | 2 +-
.../commons/internal/base/debug/XrayUiTest.java | 29 +++-
.../internal/debug/xray/CallStackMergerTest.java | 76 +++++++++
.../apache/isis/core/security/util/XrayUtil.java | 2 +-
11 files changed, 466 insertions(+), 66 deletions(-)
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/debug/_Debug.java b/commons/src/main/java/org/apache/isis/commons/internal/debug/_Debug.java
index b0ef600..225c7cf 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/debug/_Debug.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/debug/_Debug.java
@@ -94,18 +94,29 @@ public class _Debug {
// -- HELPER
- private void dump(final Object x, final int indent) {
+ private void dump(Object x, final int indent) {
if(x instanceof Iterable) {
_NullSafe.streamAutodetect(x)
.forEach(element->dump(element, indent+1));
+ return;
+ }
+ if(x!=null
+ && x.getClass().isArray()) {
+
+ val array = _NullSafe.streamAutodetect(x)
+ .map(e->""+e)
+ .collect(Collectors.joining(", "));
+
+ x = String.format("[%s]", array);
+ }
+
+ if(indent==0) {
+ System.err.printf("%s%n", x);
} else {
- if(indent==0) {
- System.err.printf("%s%n", x);
- } else {
- val suffix = _Strings.padEnd("", indent, '-');
- System.err.printf("%s %s%n", suffix, x);
- }
+ val suffix = _Strings.padEnd("", indent, '-');
+ System.err.printf("%s %s%n", suffix, x);
}
+
}
private boolean accept(final StackTraceElement se) {
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayDataModel.java b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayDataModel.java
index 0e7fb80..4176829 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayDataModel.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayDataModel.java
@@ -20,7 +20,6 @@ package org.apache.isis.commons.internal.debug.xray;
import java.awt.BorderLayout;
import java.awt.Color;
-import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;
@@ -36,7 +35,7 @@ import javax.swing.JScrollPane;
import org.apache.isis.commons.functional.IndexedConsumer;
import org.apache.isis.commons.internal.base._Refs;
import org.apache.isis.commons.internal.debug.xray.XrayModel.HasIdAndLabel;
-import org.apache.isis.commons.internal.debug.xray.sequence.SequenceDiagram;
+import org.apache.isis.commons.internal.debug.xray.graphics.SequenceDiagram;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@@ -140,6 +139,7 @@ public abstract class XrayDataModel extends HasIdAndLabel {
panel.setViewportView(panel2);
}
+
}
@@ -168,22 +168,13 @@ public abstract class XrayDataModel extends HasIdAndLabel {
@Override
public void render(final JScrollPane panel) {
- val dim = data.layout((Graphics2D)panel.getGraphics());
-
- val canvas = new JPanel() {
- private static final long serialVersionUID = 1L;
-
- @Override
- public void paintComponent(final Graphics _g) {
-
- val g = (Graphics2D)_g;
-
- g.setColor(BACKGROUND_COLOR);
- g.fillRect(0, 0, getWidth(), getHeight());
+ val canvas = _SwingUtil.canvas(g->{
+ g.setColor(BACKGROUND_COLOR);
+ g.fill(g.getClip());
+ data.render(g);
+ });
- data.render(g);
- }
- };
+ val dim = data.layout((Graphics2D)panel.getGraphics());
if(BORDER_COLOR!=null) {
canvas.setBorder(BorderFactory.createLineBorder(BORDER_COLOR));
@@ -193,8 +184,6 @@ public abstract class XrayDataModel extends HasIdAndLabel {
panel.setViewportView(canvas);
}
-
-
}
}
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayUi.java b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayUi.java
index 3c0c078..ed0d1fd 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayUi.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/XrayUi.java
@@ -18,8 +18,10 @@
*/
package org.apache.isis.commons.internal.debug.xray;
+import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
+import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
@@ -32,7 +34,9 @@ import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
+import java.util.stream.Stream;
+import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
@@ -41,13 +45,14 @@ import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
+import javax.swing.JTextArea;
import javax.swing.JTree;
+import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
-import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeCellRenderer;
import org.apache.isis.commons.collections.Can;
@@ -142,6 +147,15 @@ public class XrayUi extends JFrame {
});
val popupMenu = new JPopupMenu();
+
+ val callStackMergeAction = popupMenu.add(new JMenuItem("Merge Logged Call-Stack"));
+ callStackMergeAction.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ mergeCallStacksOnSelectedNodes();
+ }
+ });
+
val deleteAction = popupMenu.add(new JMenuItem("Delete"));
deleteAction.addActionListener(new ActionListener() {
@Override
@@ -199,10 +213,15 @@ public class XrayUi extends JFrame {
});
}
+ private Stream<DefaultMutableTreeNode> streamSelectedNodes() {
+ return Can.ofArray(tree.getSelectionModel().getSelectionPaths())
+ .stream()
+ .map(path->(DefaultMutableTreeNode)path.getLastPathComponent());
+ }
+
private void removeSelectedNodes() {
- Can.ofArray(tree.getSelectionModel().getSelectionPaths())
- .forEach(path->{
- val nodeToBeRemoved = (MutableTreeNode)path.getLastPathComponent();
+ streamSelectedNodes()
+ .forEach(nodeToBeRemoved->{
if(nodeToBeRemoved.getParent()!=null) {
((DefaultTreeModel)tree.getModel()).removeNodeFromParent(nodeToBeRemoved);
xrayModel.remove(nodeToBeRemoved);
@@ -210,6 +229,55 @@ public class XrayUi extends JFrame {
});
}
+ private void mergeCallStacksOnSelectedNodes() {
+ val logEntries = streamSelectedNodes()
+ .filter(node->node.getUserObject() instanceof XrayDataModel.LogEntry)
+ .map(node->(XrayDataModel.LogEntry)node.getUserObject())
+ .collect(Can.toCan());
+
+ if(!logEntries.getCardinality().isMultiple()) {
+ System.err.println("must select at least 2 logs for merging");
+ return;
+ }
+
+ val callStackMerger = new _CallStackMerger(logEntries);
+
+ JFrame frame = new JFrame("Merged Log View");
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.setOpaque(true);
+// val canvas = _SwingUtil.canvas(g->{
+// g.setColor(Color.GRAY);
+// g.fill(g.getClip());
+// callStackMerger.render(g);
+// });
+// JScrollPane scroller = new JScrollPane(canvas);
+
+ //Create a text area.
+ JTextArea textArea = new JTextArea(
+ "This is an editable JTextArea. " +
+ "A text area is a \"plain\" text component, " +
+ "which means that although it can display text " +
+ "in any font, all of the text is in the same font."
+ );
+ textArea.setFont(new Font("Serif", Font.PLAIN, 16));
+ textArea.setLineWrap(true);
+ textArea.setWrapStyleWord(true);
+ JScrollPane scroller = new JScrollPane(textArea);
+ callStackMerger.render(textArea);
+
+ scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
+ scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+ panel.add(scroller);
+ frame.getContentPane().add(BorderLayout.CENTER, panel);
+ frame.setPreferredSize(new Dimension(800, 600));
+ frame.pack();
+ frame.setLocationByPlatform(true);
+ frame.setVisible(true);
+ frame.setResizable(true);
+ frame.setVisible(true);
+ }
+
private JScrollPane layoutUIAndGetDetailPanel(final JTree masterTree) {
JScrollPane masterScrollPane = new JScrollPane(masterTree);
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/_CallStackMerger.java b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/_CallStackMerger.java
new file mode 100644
index 0000000..2d90585
--- /dev/null
+++ b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/_CallStackMerger.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.isis.commons.internal.debug.xray;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.IntFunction;
+
+import javax.swing.JTextArea;
+
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.functional.IndexedConsumer;
+import org.apache.isis.commons.internal.collections._Maps;
+import org.apache.isis.commons.internal.collections._Sets;
+import org.apache.isis.commons.internal.debug.xray.XrayDataModel.LogEntry;
+import org.apache.isis.commons.internal.debug.xray.graphics.CallStackDiagram;
+
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+
+@RequiredArgsConstructor
+final class _CallStackMerger {
+
+ private final Can<LogEntry> logEntries;
+ private final AtomicBoolean initialized = new AtomicBoolean(false);
+ private CallStackDiagram callStackDiagram;
+
+ void render(final JTextArea textArea) {
+ if(!initialized.get()) {
+ initialize();
+ initialized.set(true);
+ }
+ callStackDiagram.render(textArea);
+ }
+
+ static interface IntTreeVisitor {
+ void accept(int level, int value);
+ }
+
+ @RequiredArgsConstructor
+ static class IntTreeNode {
+ final int value;
+ final int level;
+ final IntTreeNode parent;
+ final List<IntTreeNode> children = new ArrayList<>();
+ IntTreeNode addChild(final int value) {
+ IntTreeNode child;
+ children.add(child = new IntTreeNode(value, level+1, this));
+ return child;
+ }
+ static IntTreeNode newRoot(final int value) {
+ return new IntTreeNode(value, 0, null);
+ }
+ void visitDepthFirst(final IntTreeVisitor visitor) {
+ visitor.accept(level, value);
+ for(val child : children) {
+ child.visitDepthFirst(visitor);
+ }
+ }
+ void visitBreadthFirst(final IntTreeVisitor visitor) {
+ val queue = new ArrayDeque<IntTreeNode>();
+ queue.add(this);
+ IntTreeNode currentNode;
+ while (!queue.isEmpty()) {
+ currentNode = queue.remove();
+ visitor.accept(currentNode.level, currentNode.value);
+ queue.addAll(currentNode.children);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return print(i->""+i).toString();
+ }
+
+ private StringBuilder print(final IntFunction<String> valueMapper) {
+ val sb = new StringBuilder();
+ print(valueMapper, sb, "", "");
+ return sb;
+ }
+
+ private void print(final IntFunction<String> valueMapper,
+ final StringBuilder buffer, final String prefix, final String childrenPrefix) {
+ buffer.append(prefix);
+ buffer.append(valueMapper.apply(value));
+ buffer.append('\n');
+ for (Iterator<IntTreeNode> it = children.iterator(); it.hasNext();) {
+ IntTreeNode next = it.next();
+ if (it.hasNext()) {
+ next.print(valueMapper, buffer, childrenPrefix + "├── ", childrenPrefix + "│ ");
+ } else {
+ next.print(valueMapper, buffer, childrenPrefix + "└── ", childrenPrefix + " ");
+ }
+ }
+ }
+
+ }
+
+ private void initialize() {
+
+ val executionNodeSet = _Sets.<String>newHashSet(); // temporary helper
+ val executionNodeMap = _Maps.<Integer, String>newHashMap(); // StackStraceElement by unique id
+
+ val executionLanes = new ArrayList<int[]>();
+
+ logEntries.forEach(logEntry->{
+ //System.err.printf("joining %s%n", logEntry.getLabel());
+
+ val executionLane = new int[logEntry.getData().size()];
+ executionLanes.add(executionLane);
+
+ Can.ofCollection(logEntry.getData()).reverse().stream()
+ .map(StackTraceElement::toString).forEach(IndexedConsumer.zeroBased((index, se)->{
+ val isNew = executionNodeSet.add(se);
+ if(isNew) {
+ final int id = executionNodeSet.size();
+ executionNodeMap.put(id, se);
+ executionLane[index] = id;
+ } else {
+ final int id = executionNodeMap.entrySet().stream()
+ .filter(entry->entry.getValue().equals(se))
+ .mapToInt(entry->(int)entry.getKey())
+ .findAny()
+ .orElseThrow();
+ executionLane[index] = id;
+ }
+ }));
+ });
+
+ val root = merge(executionLanes);
+ callStackDiagram = new CallStackDiagram(root.print(id->{
+ return executionNodeMap.getOrDefault(id, "root");
+ }).toString());
+ }
+
+ /**
+ * executionLanes look like
+ * [1, 2, 3, 4, 5, 6]
+ * [1, 2, 3, 7, 8, 6, 9]
+ * [1, 2, 3, 4, 8]
+ * ...
+ */
+ static IntTreeNode merge(final List<int[]> executionLanes) {
+ val root = IntTreeNode.newRoot(-1);
+ executionLanes.forEach(lane->{
+ var node = root;
+ for(int id : lane) {
+ val equalNode = node.children.stream().filter(child->child.value==id).findAny();
+ if(!equalNode.isPresent()) {
+ node = node.addChild(id);
+ } else {
+ node = equalNode.get();
+ }
+ }
+ });
+ return root;
+ }
+
+}
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/_SwingUtil.java b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/_SwingUtil.java
index 634f879..5670991 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/_SwingUtil.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/_SwingUtil.java
@@ -19,8 +19,12 @@
package org.apache.isis.commons.internal.debug.xray;
import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.util.Collections;
+import java.util.function.Consumer;
+import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.table.TableCellRenderer;
@@ -29,31 +33,33 @@ import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import lombok.val;
+import lombok.experimental.UtilityClass;
+@UtilityClass
final class _SwingUtil {
- static JTable newTable(final Object[][] tableData, final String[] columnNames) {
+ JTable newTable(final Object[][] tableData, final String[] columnNames) {
val table = new JTable(tableData, columnNames) {
private static final long serialVersionUID = 1L;
@Override
- public Component prepareRenderer(final TableCellRenderer renderer, final int row, final int column) {
- Component component = super.prepareRenderer(renderer, row, column);
- int rendererWidth = component.getPreferredSize().width;
- TableColumn tableColumn = getColumnModel().getColumn(column);
- tableColumn.setPreferredWidth(Math.max(rendererWidth + getIntercellSpacing().width, tableColumn.getPreferredWidth()));
- return component;
- }
- };
+ public Component prepareRenderer(final TableCellRenderer renderer, final int row, final int column) {
+ Component component = super.prepareRenderer(renderer, row, column);
+ int rendererWidth = component.getPreferredSize().width;
+ TableColumn tableColumn = getColumnModel().getColumn(column);
+ tableColumn.setPreferredWidth(Math.max(rendererWidth + getIntercellSpacing().width, tableColumn.getPreferredWidth()));
+ return component;
+ }
+ };
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
return table;
}
- static void setTreeExpandedState(final JTree tree, final boolean expanded) {
+ void setTreeExpandedState(final JTree tree, final boolean expanded) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getModel().getRoot();
setNodeExpandedState(tree, node, expanded);
}
- static void setNodeExpandedState(final JTree tree, final DefaultMutableTreeNode node, final boolean expanded) {
+ void setNodeExpandedState(final JTree tree, final DefaultMutableTreeNode node, final boolean expanded) {
for (Object treeNode : Collections.list(node.children())) {
setNodeExpandedState(tree, (DefaultMutableTreeNode) treeNode, expanded);
}
@@ -68,4 +74,17 @@ final class _SwingUtil {
}
}
+ JPanel canvas(final Consumer<Graphics2D> onRender) {
+
+ val canvas = new JPanel() {
+ private static final long serialVersionUID = 1L;
+ @Override
+ public void paintComponent(final Graphics g) {
+ onRender.accept((Graphics2D)g);
+ }
+ };
+
+ return canvas;
+ }
+
}
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/graphics/CallStackDiagram.java b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/graphics/CallStackDiagram.java
new file mode 100644
index 0000000..efcdaa9
--- /dev/null
+++ b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/graphics/CallStackDiagram.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.isis.commons.internal.debug.xray.graphics;
+
+import java.awt.Font;
+
+import javax.swing.JTextArea;
+
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public class CallStackDiagram {
+
+ private final String textContent;
+
+ public void render(final JTextArea textArea) {
+ textArea.setFont(new Font("Consolas", Font.PLAIN, 13));
+ textArea.setText(textContent);
+ }
+
+}
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/sequence/SequenceDiagram.java b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/graphics/SequenceDiagram.java
similarity index 91%
rename from commons/src/main/java/org/apache/isis/commons/internal/debug/xray/sequence/SequenceDiagram.java
rename to commons/src/main/java/org/apache/isis/commons/internal/debug/xray/graphics/SequenceDiagram.java
index ea97748..e274c3e 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/sequence/SequenceDiagram.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/graphics/SequenceDiagram.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.isis.commons.internal.debug.xray.sequence;
+package org.apache.isis.commons.internal.debug.xray.graphics;
import java.awt.Color;
import java.awt.Dimension;
@@ -32,7 +32,7 @@ import java.util.TreeMap;
import org.apache.isis.commons.collections.Can;
import org.apache.isis.commons.internal.base._Refs;
import org.apache.isis.commons.internal.base._Refs.IntReference;
-import org.apache.isis.commons.internal.debug.xray.sequence._Graphics.TextBlock;
+import org.apache.isis.commons.internal.debug.xray.graphics._Graphics.TextBlock;
import org.apache.isis.commons.internal.primitives._Ints;
import lombok.Getter;
@@ -50,38 +50,38 @@ public class SequenceDiagram {
private Dimension size;
- public SequenceDiagram alias(String id, String label) {
+ public SequenceDiagram alias(final String id, final String label) {
aliases.put(id, label);
return this;
}
- public void enter(final @NonNull String from, final @NonNull String to, String label) {
+ public void enter(final @NonNull String from, final @NonNull String to, final String label) {
val p0 = participant(from);
val p1 = participant(to);
connections.add(newConnection(p0, p1, label, false));
}
- public void exit(final @NonNull String from, final @NonNull String to, String label) {
+ public void exit(final @NonNull String from, final @NonNull String to, final String label) {
val p1 = participant(to);
val p0 = participant(from);
connections.add(newConnection(p0, p1, label, true));
}
- public void enter(String from, String to) {
+ public void enter(final String from, final String to) {
enter(from, to, null);
}
- public void exit(String from, String to) {
+ public void exit(final String from, final String to) {
exit(from, to, null);
}
- public void activate(String participantId) {
+ public void activate(final String participantId) {
val participant = participant(participantId);
val latestConnection = latestConnection();
lifelines.add(new Lifeline(participant, latestConnection));
}
- public void deactivate(String participantId) {
+ public void deactivate(final String participantId) {
val participant = participant(participantId);
val latestConnection = latestConnection();
Can.ofCollection(lifelines).reverse().stream()
@@ -95,11 +95,11 @@ public class SequenceDiagram {
private Color connectionArrowColor;
private Color connectionLabelColor;
- public void setConnectionArrowColor(Color connectionArrowColor) {
+ public void setConnectionArrowColor(final Color connectionArrowColor) {
this.connectionArrowColor = connectionArrowColor;
}
- public void setConnectionLabelColor(Color connectionLabelColor) {
+ public void setConnectionLabelColor(final Color connectionLabelColor) {
this.connectionLabelColor = connectionLabelColor;
}
@@ -120,7 +120,7 @@ public class SequenceDiagram {
getConnectionLabelColor());
}
- private Participant participant(String participantId) {
+ private Participant participant(final String participantId) {
return participantsById
.computeIfAbsent(participantId, id->new Participant(aliases.getOrDefault(id, id)));
}
@@ -186,7 +186,7 @@ public class SequenceDiagram {
int y_bottom;
int height;
- void layout(Graphics2D g, IntReference y_offset, List<Lifeline> lifelines) {
+ void layout(final Graphics2D g, final IntReference y_offset, final List<Lifeline> lifelines) {
x_from = from.getX_middle();
x_to = to.getX_middle();
@@ -244,7 +244,7 @@ public class SequenceDiagram {
TextBlock textBlock;
- void layout(Graphics2D g, IntReference x_offset) {
+ void layout(final Graphics2D g, final IntReference x_offset) {
x_left = x_offset.getValue();
y_top = PARTICIPANT_MARGIN_V;
@@ -282,7 +282,7 @@ public class SequenceDiagram {
int y_bottom;
int height;
- void layout(Graphics2D g, int min_y, int max_y) {
+ void layout(final Graphics2D g, final int min_y, final int max_y) {
width = LIFELINE_WIDTH;
x_left = participant.getX_middle() - LIFELINE_WIDTH / 2;
@@ -298,7 +298,7 @@ public class SequenceDiagram {
height = y_bottom - y_top;
}
- public boolean overlaps(Connection connection) {
+ public boolean overlaps(final Connection connection) {
val lowerBound = _Ints.Bound.inclusive(startAt != null
? startAt.index
: -1);
@@ -310,7 +310,7 @@ public class SequenceDiagram {
}
- public Dimension layout(Graphics2D g) {
+ public Dimension layout(final Graphics2D g) {
PARTICIPANT_FONT.ifPresent(g::setFont);
@@ -343,7 +343,7 @@ public class SequenceDiagram {
return this.size;
}
- public void render(Graphics2D g) {
+ public void render(final Graphics2D g) {
_Graphics.enableTextAntialiasing(g);
@@ -416,7 +416,4 @@ public class SequenceDiagram {
}
-
-
-
}
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/sequence/_Graphics.java b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/graphics/_Graphics.java
similarity index 98%
rename from commons/src/main/java/org/apache/isis/commons/internal/debug/xray/sequence/_Graphics.java
rename to commons/src/main/java/org/apache/isis/commons/internal/debug/xray/graphics/_Graphics.java
index fdfc6b4..31edcb1 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/sequence/_Graphics.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/graphics/_Graphics.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.isis.commons.internal.debug.xray.sequence;
+package org.apache.isis.commons.internal.debug.xray.graphics;
import java.awt.BasicStroke;
import java.awt.Color;
diff --git a/commons/src/test/java/org/apache/isis/commons/internal/base/debug/XrayUiTest.java b/commons/src/test/java/org/apache/isis/commons/internal/base/debug/XrayUiTest.java
index d98daef..a026ec3 100644
--- a/commons/src/test/java/org/apache/isis/commons/internal/base/debug/XrayUiTest.java
+++ b/commons/src/test/java/org/apache/isis/commons/internal/base/debug/XrayUiTest.java
@@ -18,8 +18,13 @@
*/
package org.apache.isis.commons.internal.base.debug;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
import javax.swing.JFrame;
+import org.apache.isis.commons.collections.Can;
import org.apache.isis.commons.internal.debug._Debug;
import org.apache.isis.commons.internal.debug.xray.XrayDataModel;
import org.apache.isis.commons.internal.debug.xray.XrayModel;
@@ -29,14 +34,34 @@ import lombok.val;
class XrayUiTest {
- public static void main(final String[] args) {
+ public static void main(final String[] args) throws InterruptedException {
XrayUi.start(JFrame.EXIT_ON_CLOSE);
- _Debug.log("%s", "Hallo World!");
+ val ex = Executors.newSingleThreadExecutor();
+ ex.execute(new SampleLogs());
+ ex.shutdown();
+ ex.awaitTermination(1L, TimeUnit.SECONDS);
XrayUi.updateModel(XrayUiTest::populate);
}
+ private static class SampleLogs implements Runnable {
+
+ @Override
+ public void run() {
+ Can.of("Hallo World! from Can")
+ .forEach(this::log);
+
+ List.of("Hallo World! from List")
+ .forEach(this::log);
+ }
+
+ private void log(final String x) {
+ _Debug.log(x);
+ }
+
+ }
+
private static void populate(final XrayModel model) {
val root = model.getRootNode();
diff --git a/commons/src/test/java/org/apache/isis/commons/internal/debug/xray/CallStackMergerTest.java b/commons/src/test/java/org/apache/isis/commons/internal/debug/xray/CallStackMergerTest.java
new file mode 100644
index 0000000..35046e2
--- /dev/null
+++ b/commons/src/test/java/org/apache/isis/commons/internal/debug/xray/CallStackMergerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.isis.commons.internal.debug.xray;
+
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.apache.isis.commons.internal.debug.xray._CallStackMerger.IntTreeVisitor;
+
+import lombok.val;
+
+class CallStackMergerTest {
+
+ private List<int[]> executionLanes;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ executionLanes = List.of(
+ new int[] {1, 2, 3, 4, 5, 6},
+ new int[] {1, 2, 3, 7, 8, 6, 9},
+ new int[] {1, 2, 3, 4, 8}
+ );
+ }
+
+ /**
+ * expected ...<pre>
+-1
+└── 1
+ └── 2
+ └── 3
+ ├── 4
+ │ ├── 5
+ │ │ └── 6
+ │ └── 8
+ └── 7
+ └── 8
+ └── 6
+ └── 9
+ </pre>
+ */
+ @Test
+ void test() {
+ val root = _CallStackMerger.merge(executionLanes);
+ //System.err.printf("%s%n", root);
+
+ val sb = new StringBuilder();
+ root.visitDepthFirst(new IntTreeVisitor() {
+ @Override
+ public void accept(final int level, final int value) {
+ sb.append(level).append(":").append(value).append(",");
+ }
+ });
+ assertEquals("0:-1,1:1,2:2,3:3,4:4,5:5,6:6,5:8,4:7,5:8,6:6,7:9,", sb.toString());
+ }
+
+}
diff --git a/core/security/src/main/java/org/apache/isis/core/security/util/XrayUtil.java b/core/security/src/main/java/org/apache/isis/core/security/util/XrayUtil.java
index 092e99b..b5b2f10 100644
--- a/core/security/src/main/java/org/apache/isis/core/security/util/XrayUtil.java
+++ b/core/security/src/main/java/org/apache/isis/core/security/util/XrayUtil.java
@@ -25,8 +25,8 @@ import java.util.function.Consumer;
import org.apache.isis.applib.services.iactn.InteractionProvider;
import org.apache.isis.commons.collections.Can;
import org.apache.isis.commons.internal.debug.xray.XrayModel.ThreadMemento;
+import org.apache.isis.commons.internal.debug.xray.graphics.SequenceDiagram;
import org.apache.isis.commons.internal.debug.xray.XrayUi;
-import org.apache.isis.commons.internal.debug.xray.sequence.SequenceDiagram;
import org.apache.isis.commons.internal.exceptions._Exceptions;
import lombok.Builder;