From 8feec32e9d5532434b472a0d3f51f73ba840adad Mon Sep 17 00:00:00 2001 From: Jeff Wallace Date: Mon, 16 Sep 2013 12:36:52 -0400 Subject: [PATCH 1/7] Loading large amounts of Nodes no longer blocks the UI. --- .../corecomponents/DataResultPanel.java | 2 +- .../corecomponents/DataResultViewerTable.java | 90 +++++++++++++++---- .../datamodel/FileSearchFilterChildren.java | 15 ++-- 3 files changed, 81 insertions(+), 26 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java index 18a13ec560..4f21a1ce5a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java @@ -287,7 +287,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C public void setNode(Node selectedNode) { this.rootNode = selectedNode; if (selectedNode != null) { - int childrenCount = selectedNode.getChildren().getNodesCount(true); + int childrenCount = selectedNode.getChildren().getNodesCount(); this.numberMatchLabel.setText(Integer.toString(childrenCount)); } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 36ccb8715d..3fc82abb00 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -22,14 +22,17 @@ import java.awt.Cursor; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.dnd.DnDConstants; +import java.beans.PropertyChangeEvent; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JTable; import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; import org.netbeans.swing.outline.DefaultOutlineModel; import org.openide.explorer.ExplorerManager; import org.openide.explorer.view.OutlineView; @@ -38,6 +41,10 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Node.Property; import org.openide.nodes.Node.PropertySet; +import org.openide.nodes.NodeEvent; +import org.openide.nodes.NodeListener; +import org.openide.nodes.NodeMemberEvent; +import org.openide.nodes.NodeReorderEvent; import org.openide.nodes.Sheet; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; @@ -53,6 +60,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private String firstColumnLabel = "Name"; private Set propertiesAcc = new LinkedHashSet<>(); private static final Logger logger = Logger.getLogger(DataResultViewerTable.class.getName()); + private final DummyNodeListener dummyNodeListener = new DummyNodeListener(); /** * Creates a DataResultViewerTable object that is compatible with node @@ -246,11 +254,38 @@ public class DataResultViewerTable extends AbstractDataResultViewer { hasChildren = selectedNode.getChildren().getNodesCount() > 0; } - + Node oldNode = this.em.getRootContext(); + if (oldNode != null) { + oldNode.removeNodeListener(dummyNodeListener); + } + // if there's no selection node, do nothing if (hasChildren) { Node root = selectedNode; - + root.addNodeListener(dummyNodeListener); + setupTable(root); + } else { + final OutlineView ov = ((OutlineView) this.tableScrollPanel); + Node emptyNode = new AbstractNode(Children.LEAF); + em.setRootContext(emptyNode); // make empty node + ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + ov.setPropertyColumns(); // set the empty property header + } + } finally { + this.setCursor(null); + } + } + + /** + * Create Column Headers based on the Content represented by the Nodes in + * the table. + * + * @param root The parent Node of the ContentNodes + */ + private void setupTable(final Node root) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { //wrap to filter out children //note: this breaks the tree view mode in this generic viewer, //so wrap nodes earlier if want 1 level view @@ -261,11 +296,11 @@ public class DataResultViewerTable extends AbstractDataResultViewer { em.setRootContext(root); - final OutlineView ov = ((OutlineView) this.tableScrollPanel); + final OutlineView ov = ((OutlineView) DataResultViewerTable.this.tableScrollPanel); propertiesAcc.clear(); - this.getAllChildPropertyHeadersRec(selectedNode, 100); + DataResultViewerTable.this.getAllChildPropertyHeadersRec(root, 100); List props = new ArrayList(propertiesAcc); if (props.size() > 0) { Node.Property prop = props.remove(0); @@ -315,7 +350,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { // get first 100 rows values for the table Object[][] content = null; - content = getRowValues(selectedNode, 100); + content = getRowValues(root, 100); if (content != null) { @@ -341,17 +376,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { // turn on the auto resize ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); } - - } else { - final OutlineView ov = ((OutlineView) this.tableScrollPanel); - Node emptyNode = new AbstractNode(Children.LEAF); - em.setRootContext(emptyNode); // make empty node - ov.getOutline().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); - ov.setPropertyColumns(); // set the empty property header } - } finally { - this.setCursor(null); - } + }); } private static Object[][] getRowValues(Node node, int rows) { @@ -454,4 +480,38 @@ public class DataResultViewerTable extends AbstractDataResultViewer { super.clearComponent(); } + + private class DummyNodeListener implements NodeListener { + private static final String DUMMY_NODE_DISPLAY_NAME = "Please Wait..."; + + @Override + public void childrenAdded(NodeMemberEvent nme) { + } + + @Override + public void childrenRemoved(NodeMemberEvent nme) { + Node removed = nme.getDelta()[0]; + if (! removed.getDisplayName().equals(DUMMY_NODE_DISPLAY_NAME)) { + // If it's not the dummy waiting node, we don't want + // to reload the table headers + return; + } + + // dummy node removed. Reset the table headers. + Node node = nme.getNode(); + setupTable(node); + } + + @Override + public void childrenReordered(NodeReorderEvent nre) { + } + + @Override + public void nodeDestroyed(NodeEvent ne) { + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileSearchFilterChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileSearchFilterChildren.java index cc4f6309f0..0b351ea5bc 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileSearchFilterChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileSearchFilterChildren.java @@ -25,7 +25,6 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Node; -import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentVisitor; import org.sleuthkit.datamodel.DerivedFile; @@ -45,7 +44,7 @@ class FileSearchFilterChildren extends ChildFactory { private SleuthkitCase skCase; private SearchFilters.SearchFilterInterface filter; private static final Logger logger = Logger.getLogger(FileSearchFilterChildren.class.getName()); - //private final static int MAX_OBJECTS = 2000; +// private final static int MAX_OBJECTS = 2000; public FileSearchFilterChildren(SearchFilters.SearchFilterInterface filter, SleuthkitCase skCase) { this.filter = filter; @@ -57,7 +56,6 @@ class FileSearchFilterChildren extends ChildFactory { list.addAll(runQuery()); return true; } - private String createQuery(){ String query = "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")" @@ -66,18 +64,15 @@ class FileSearchFilterChildren extends ChildFactory { query += " OR name LIKE '%" + s + "'"; } query += ')'; - //query += " LIMIT " + MAX_OBJECTS; +// query += " LIMIT " + MAX_OBJECTS; return query; } - private List runQuery(){ - List list = new ArrayList(); + private List runQuery(){ + List list = new ArrayList<>(); try { - List res = skCase.findAllFilesWhere(createQuery()); - for(AbstractFile c : res){ - list.add(c); - } + list = skCase.findAllFilesWhere(createQuery()); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Couldn't get search results", ex); } From 260776a2910f6a3b4cd47c16045bdaf6ee0aa8d6 Mon Sep 17 00:00:00 2001 From: Jeff Wallace Date: Tue, 17 Sep 2013 12:12:13 -0400 Subject: [PATCH 2/7] Added cap to number of Nodes generated in DeletedContent --- Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java index 72f8bd451c..5b6d47906c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java @@ -213,6 +213,8 @@ public class DeletedContent implements AutopsyVisitableItem { private SleuthkitCase skCase; private DeletedContent.DeletedContentFilter filter; private final Logger logger = Logger.getLogger(DeletedContentChildren.class.getName()); + + private static final int MAX_OBJECTS = 2000; DeletedContentChildren(DeletedContent.DeletedContentFilter filter, SleuthkitCase skCase) { this.skCase = skCase; @@ -258,6 +260,7 @@ public class DeletedContent implements AutopsyVisitableItem { } + query += " LIMIT " + MAX_OBJECTS; return query; } From 7e896c073123c453320ae375ca25373dd4d87ecd Mon Sep 17 00:00:00 2001 From: Jeff Wallace Date: Tue, 17 Sep 2013 12:35:13 -0400 Subject: [PATCH 3/7] DataResultPanel now checks which DRV are supported for the current Node when the children are done loading. --- .../corecomponents/DataResultPanel.java | 57 ++++++++++++++++--- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java index 4f21a1ce5a..bc2e4f9282 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.corecomponents; import java.awt.Cursor; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; @@ -29,6 +30,10 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; +import org.openide.nodes.NodeEvent; +import org.openide.nodes.NodeListener; +import org.openide.nodes.NodeMemberEvent; +import org.openide.nodes.NodeReorderEvent; import org.openide.util.Lookup; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; @@ -58,6 +63,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C private DataContent customContentViewer; private boolean isMain; private String title; + private final DummyNodeListener dummyNodeListener = new DummyNodeListener(); private static final Logger logger = Logger.getLogger(DataResultPanel.class.getName() ); @@ -285,7 +291,16 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C @Override public void setNode(Node selectedNode) { + if (this.rootNode != null) { + this.rootNode.removeNodeListener(dummyNodeListener); + } this.rootNode = selectedNode; + if (this.rootNode != null) { + this.rootNode.addNodeListener(dummyNodeListener); + } + + setupTabs(selectedNode); + if (selectedNode != null) { int childrenCount = selectedNode.getChildren().getNodesCount(); this.numberMatchLabel.setText(Integer.toString(childrenCount)); @@ -295,7 +310,16 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C this.numberMatchLabel.setVisible(true); resetTabs(selectedNode); - + + // set the display on the current active tab + int currentActiveTab = this.dataResultTabbedPanel.getSelectedIndex(); + if (currentActiveTab != -1) { + UpdateWrapper drv = viewers.get(currentActiveTab); + drv.setNode(selectedNode); + } + } + + private void setupTabs(Node selectedNode) { //update/disable tabs based on if supported for this node int drvC = 0; for (UpdateWrapper drv : viewers) { @@ -307,13 +331,6 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C } ++drvC; } - - // set the display on the current active tab - int currentActiveTab = this.dataResultTabbedPanel.getSelectedIndex(); - if (currentActiveTab != -1) { - UpdateWrapper drv = viewers.get(currentActiveTab); - drv.setNode(selectedNode); - } } @Override @@ -499,4 +516,28 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C public void setNumMatches(int numMatches) { this.numberMatchLabel.setText(Integer.toString(numMatches)); } + + private class DummyNodeListener implements NodeListener { + + @Override + public void childrenAdded(NodeMemberEvent nme) { + setupTabs(nme.getNode()); + } + + @Override + public void childrenRemoved(NodeMemberEvent nme) { + } + + @Override + public void childrenReordered(NodeReorderEvent nre) { + } + + @Override + public void nodeDestroyed(NodeEvent ne) { + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + } + } } From 4b0fe6dd63e75d6e4cddc54423f4f332ba3b807e Mon Sep 17 00:00:00 2001 From: Jeff Wallace Date: Wed, 18 Sep 2013 16:13:02 -0400 Subject: [PATCH 4/7] Moved adding of jdkhome property to the windows installer targets. --- build-windows.xml | 11 +++++++++++ build.xml | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/build-windows.xml b/build-windows.xml index 330321f372..f048ee98b2 100644 --- a/build-windows.xml +++ b/build-windows.xml @@ -27,6 +27,17 @@ + + + + + + + + + + + diff --git a/build.xml b/build.xml index 11f80417cb..417487251e 100644 --- a/build.xml +++ b/build.xml @@ -78,7 +78,6 @@ - From 393f9180fbe648e1e50c6ea39ab4e870b75cf000 Mon Sep 17 00:00:00 2001 From: Jeff Wallace Date: Thu, 19 Sep 2013 10:55:20 -0400 Subject: [PATCH 5/7] Corrected gstreamer env variable in build script. --- build-windows.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-windows.xml b/build-windows.xml index f048ee98b2..dc7cec7600 100644 --- a/build-windows.xml +++ b/build-windows.xml @@ -185,7 +185,7 @@ - + From 0370fddf57623a4aab92ebeeed851789ec96dfa3 Mon Sep 17 00:00:00 2001 From: Jeff Wallace Date: Thu, 19 Sep 2013 13:42:44 -0400 Subject: [PATCH 6/7] Added popup dialog to DeletedCotent to alert the user that not all deleted files are shown. --- .../autopsy/datamodel/DeletedContent.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java index 5b6d47906c..66390045ee 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java @@ -22,6 +22,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; @@ -214,7 +216,7 @@ public class DeletedContent implements AutopsyVisitableItem { private DeletedContent.DeletedContentFilter filter; private final Logger logger = Logger.getLogger(DeletedContentChildren.class.getName()); - private static final int MAX_OBJECTS = 2000; + private static final int MAX_OBJECTS = 2001; DeletedContentChildren(DeletedContent.DeletedContentFilter filter, SleuthkitCase skCase) { this.skCase = skCase; @@ -223,7 +225,20 @@ public class DeletedContent implements AutopsyVisitableItem { @Override protected boolean createKeys(List list) { - list.addAll(runFsQuery()); + List queryList = runFsQuery(); + if (queryList.size() == MAX_OBJECTS) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + JOptionPane.showMessageDialog(null, "There are more Deleted Files than can be displayed. Only the first " + + (MAX_OBJECTS - 1) + + " Deleted Files will be shown."); + } + }); + } + + queryList.remove(queryList.size() - 1); + list.addAll(queryList); return true; } From 81b5761684f77a44123fc49b87876944b44a137c Mon Sep 17 00:00:00 2001 From: Jeff Wallace Date: Fri, 20 Sep 2013 15:08:36 -0400 Subject: [PATCH 7/7] Added fix to ensure DataResultPanel sets up activated tabs on the EDT. Cleaned up NodeListener logic. --- .../corecomponents/DataResultPanel.java | 73 +++++++++++-------- .../corecomponents/DataResultViewerTable.java | 18 ++--- 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java index 23ded6576d..d44eed57a7 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openide.explorer.ExplorerManager; @@ -379,40 +380,46 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C } } - private void setupTabs(Node selectedNode) { - //update/disable tabs based on if supported for this node - int drvC = 0; - for (UpdateWrapper drv : viewers) { + private void setupTabs(final Node selectedNode) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + //update/disable tabs based on if supported for this node + int drvC = 0; + for (UpdateWrapper drv : viewers) { - if (drv.isSupported(selectedNode)) { - dataResultTabbedPanel.setEnabledAt(drvC, true); - } else { - dataResultTabbedPanel.setEnabledAt(drvC, false); - } - ++drvC; - } + if (drv.isSupported(selectedNode)) { + dataResultTabbedPanel.setEnabledAt(drvC, true); + } else { + dataResultTabbedPanel.setEnabledAt(drvC, false); + } + ++drvC; + } - // if the current tab is no longer enabled, then find one that is - boolean hasViewerEnabled = true; - int currentActiveTab = this.dataResultTabbedPanel.getSelectedIndex(); - if ((currentActiveTab == -1) || (dataResultTabbedPanel.isEnabledAt(currentActiveTab) == false)) { - hasViewerEnabled = false; - for (int i = 0; i < dataResultTabbedPanel.getTabCount(); i++) { - if (dataResultTabbedPanel.isEnabledAt(i)) { - currentActiveTab = i; - hasViewerEnabled = true; - break; + // if the current tab is no longer enabled, then find one that is + boolean hasViewerEnabled = true; + int currentActiveTab = dataResultTabbedPanel.getSelectedIndex(); + if ((currentActiveTab == -1) || (dataResultTabbedPanel.isEnabledAt(currentActiveTab) == false)) { + hasViewerEnabled = false; + for (int i = 0; i < dataResultTabbedPanel.getTabCount(); i++) { + if (dataResultTabbedPanel.isEnabledAt(i)) { + currentActiveTab = i; + hasViewerEnabled = true; + break; + } + } + + if (hasViewerEnabled) { + dataResultTabbedPanel.setSelectedIndex(currentActiveTab); + } + } + + if (hasViewerEnabled) { + viewers.get(currentActiveTab).setNode(selectedNode); } } - - if (hasViewerEnabled) { - dataResultTabbedPanel.setSelectedIndex(currentActiveTab); - } - } + }); - if (hasViewerEnabled) { - viewers.get(currentActiveTab).setNode(selectedNode); - } } @Override @@ -604,9 +611,15 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C } private class DummyNodeListener implements NodeListener { + private static final String DUMMY_NODE_DISPLAY_NAME = "Please Wait..."; @Override - public void childrenAdded(NodeMemberEvent nme) { + public void childrenAdded(final NodeMemberEvent nme) { + Node added = nme.getNode(); + if (added.getDisplayName().equals(DUMMY_NODE_DISPLAY_NAME)) { + // don't set up tabs if the new node is a waiting node + return; + } setupTabs(nme.getNode()); } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index ac29e7008e..6c42c9f7bf 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -481,20 +481,18 @@ public class DataResultViewerTable extends AbstractDataResultViewer { @Override public void childrenAdded(NodeMemberEvent nme) { + Node added = nme.getNode(); + if (added.getDisplayName().equals(DUMMY_NODE_DISPLAY_NAME)) { + // If it's the dummy waiting node, we don't want + // to reload the table headers + return; + } + setupTable(added); } @Override public void childrenRemoved(NodeMemberEvent nme) { - Node removed = nme.getDelta()[0]; - if (! removed.getDisplayName().equals(DUMMY_NODE_DISPLAY_NAME)) { - // If it's not the dummy waiting node, we don't want - // to reload the table headers - return; - } - - // dummy node removed. Reset the table headers. - Node node = nme.getNode(); - setupTable(node); + } @Override