Extracted text viewer: use background worker to get text from Solr

This commit is contained in:
adam-m 2012-11-01 11:09:17 -04:00
parent f498b9cfe7
commit cd00782f44
2 changed files with 244 additions and 191 deletions

View File

@ -23,10 +23,10 @@ import java.awt.event.ActionListener;
import java.awt.event.ItemEvent; import java.awt.event.ItemEvent;
import java.awt.event.ItemListener; import java.awt.event.ItemListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.SwingWorker;
import javax.swing.text.AbstractDocument; import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet; import javax.swing.text.AttributeSet;
import javax.swing.text.Element; import javax.swing.text.Element;
@ -36,10 +36,13 @@ import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTML; import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.HTMLEditorKit.HTMLFactory; import javax.swing.text.html.HTMLEditorKit.HTMLFactory;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.Cancellable;
/** /**
* Panel displays HTML content sent to ExtractedContentViewer, and provides * Panel displays HTML content sent to ExtractedContentViewer, and provides a
* a combo-box to select between multiple sources. * combo-box to select between multiple sources.
*/ */
class ExtractedContentPanel extends javax.swing.JPanel { class ExtractedContentPanel extends javax.swing.JPanel {
@ -56,9 +59,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
private void customizeComponents() { private void customizeComponents() {
extractedTextPane.setEditorKit(new HTMLEditorKit() { extractedTextPane.setEditorKit(new HTMLEditorKit() {
ViewFactory viewFactory = new HTMLFactory() { ViewFactory viewFactory = new HTMLFactory() {
@Override @Override
public View create(Element elem) { public View create(Element elem) {
AttributeSet attrs = elem.getAttributes(); AttributeSet attrs = elem.getAttributes();
@ -81,12 +82,12 @@ class ExtractedContentPanel extends javax.swing.JPanel {
}); });
sourceComboBox.addItemListener(new ItemListener() { sourceComboBox.addItemListener(new ItemListener() {
@Override @Override
public void itemStateChanged(ItemEvent e) { public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) { if (e.getStateChange() == ItemEvent.SELECTED) {
MarkupSource source = (MarkupSource) e.getItem(); MarkupSource source = (MarkupSource) e.getItem();
setPanelText(source.getMarkup()); //setPanelText(source.getMarkup());
new SetMarkup(source).execute();
} }
} }
}); });
@ -95,24 +96,25 @@ class ExtractedContentPanel extends javax.swing.JPanel {
setSources(new ArrayList<MarkupSource>()); setSources(new ArrayList<MarkupSource>());
extractedTextPane.setComponentPopupMenu(rightClickMenu); extractedTextPane.setComponentPopupMenu(rightClickMenu);
ActionListener actList = new ActionListener(){ ActionListener actList = new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e){ public void actionPerformed(ActionEvent e) {
JMenuItem jmi = (JMenuItem) e.getSource(); JMenuItem jmi = (JMenuItem) e.getSource();
if(jmi.equals(copyMenuItem)) if (jmi.equals(copyMenuItem)) {
extractedTextPane.copy(); extractedTextPane.copy();
else if(jmi.equals(selectAllMenuItem)) } else if (jmi.equals(selectAllMenuItem)) {
extractedTextPane.selectAll(); extractedTextPane.selectAll();
} }
}
}; };
copyMenuItem.addActionListener(actList); copyMenuItem.addActionListener(actList);
selectAllMenuItem.addActionListener(actList); selectAllMenuItem.addActionListener(actList);
} }
/** This method is called from within the constructor to /**
* initialize the form. * This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is * WARNING: Do NOT modify this code. The content of this method is always
* always regenerated by the Form Editor. * regenerated by the Form Editor.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
@ -282,7 +284,6 @@ class ExtractedContentPanel extends javax.swing.JPanel {
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 293, Short.MAX_VALUE)) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 293, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JMenuItem copyMenuItem; private javax.swing.JMenuItem copyMenuItem;
private javax.swing.JTextPane extractedTextPane; private javax.swing.JTextPane extractedTextPane;
@ -306,15 +307,16 @@ class ExtractedContentPanel extends javax.swing.JPanel {
private javax.swing.JComboBox sourceComboBox; private javax.swing.JComboBox sourceComboBox;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
void refreshCurrentMarkup() { void refreshCurrentMarkup() {
MarkupSource ms = (MarkupSource)sourceComboBox.getSelectedItem(); MarkupSource ms = (MarkupSource) sourceComboBox.getSelectedItem();
setPanelText(ms.getMarkup()); //setPanelText(ms.getMarkup());
new SetMarkup(ms).execute();
} }
/** /**
* Set the available sources (selects the first source in the list by * Set the available sources (selects the first source in the list by
* default) * default)
*
* @param sources * @param sources
*/ */
void setSources(List<MarkupSource> sources) { void setSources(List<MarkupSource> sources) {
@ -360,7 +362,6 @@ class ExtractedContentPanel extends javax.swing.JPanel {
hitNextButton.setEnabled(false); hitNextButton.setEnabled(false);
} }
void scrollToAnchor(String anchor) { void scrollToAnchor(String anchor) {
extractedTextPane.scrollToReference(anchor); extractedTextPane.scrollToReference(anchor);
} }
@ -370,9 +371,11 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* @param current, current hit to update the display with * @param current, current hit to update the display with
*/ */
void updateCurrentMatchDisplay(int current) { void updateCurrentMatchDisplay(int current) {
if (current == 0) if (current == 0) {
hitCountLabel.setText("-"); hitCountLabel.setText("-");
else hitCountLabel.setText(Integer.toString(current)); } else {
hitCountLabel.setText(Integer.toString(current));
}
} }
/** /**
@ -380,11 +383,12 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* @param total total number of hits to update the display with * @param total total number of hits to update the display with
*/ */
void updateTotaMatcheslDisplay(int total) { void updateTotaMatcheslDisplay(int total) {
if (total == 0) if (total == 0) {
hitTotalLabel.setText("-"); hitTotalLabel.setText("-");
else hitTotalLabel.setText(Integer.toString(total)); } else {
hitTotalLabel.setText(Integer.toString(total));
}
} }
/** /**
* *
@ -402,7 +406,6 @@ class ExtractedContentPanel extends javax.swing.JPanel {
pageTotalLabel.setText(Integer.toString(total)); pageTotalLabel.setText(Integer.toString(total));
} }
void resetDisplay() { void resetDisplay() {
resetHitDisplay(); resetHitDisplay();
resetPagesDisplay(); resetPagesDisplay();
@ -426,6 +429,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
/** /**
* enable previous match control * enable previous match control
*
* @param enable whether to enable or disable * @param enable whether to enable or disable
*/ */
void enablePrevMatchControl(boolean enable) { void enablePrevMatchControl(boolean enable) {
@ -434,6 +438,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
/** /**
* enable next match control * enable next match control
*
* @param enable whether to enable or disable * @param enable whether to enable or disable
*/ */
void enableNextMatchControl(boolean enable) { void enableNextMatchControl(boolean enable) {
@ -448,9 +453,9 @@ class ExtractedContentPanel extends javax.swing.JPanel {
hitNextButton.addActionListener(l); hitNextButton.addActionListener(l);
} }
/** /**
* enable previous oage control * enable previous oage control
*
* @param enable whether to enable or disable * @param enable whether to enable or disable
*/ */
void enablePrevPageControl(boolean enable) { void enablePrevPageControl(boolean enable) {
@ -459,6 +464,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
/** /**
* enable next page control * enable next page control
*
* @param enable whether to enable or disable * @param enable whether to enable or disable
*/ */
void enableNextPageControl(boolean enable) { void enableNextPageControl(boolean enable) {
@ -476,4 +482,43 @@ class ExtractedContentPanel extends javax.swing.JPanel {
void addSourceComboControlListener(ActionListener l) { void addSourceComboControlListener(ActionListener l) {
sourceComboBox.addActionListener(l); sourceComboBox.addActionListener(l);
} }
/**
* Swingworker to get makrup source content String from Solr in background thread
* and then set the panel text in the EDT
* Helps not to block the UI while content from Solr is retrieved.
*/
private final class SetMarkup extends SwingWorker<Object, Void> {
private MarkupSource source;
private String markup;
private ProgressHandle progress;
SetMarkup(MarkupSource source) {
this.source = source;
}
@Override
protected Object doInBackground() throws Exception {
progress = ProgressHandleFactory.createHandle("Loading text");
progress.setDisplayName("Loading text");
progress.start();
progress.switchToIndeterminate();
setPanelText("<span style='font-style:italic'>Loading text... Please wait</span>");
markup = source.getMarkup();
return null;
}
@Override
protected void done() {
super.done();
progress.finish();
if (markup != null) {
setPanelText(markup);
} else {
setPanelText("");
}
}
}
} }

View File

@ -42,10 +42,10 @@ import org.sleuthkit.datamodel.Directory;
/** /**
* Displays marked-up (HTML) content for a Node. The sources are all the * Displays marked-up (HTML) content for a Node. The sources are all the
* MarkupSource items in the selected Node's lookup, plus the content that * MarkupSource items in the selected Node's lookup, plus the content that Solr
* Solr extracted (if there is any). * extracted (if there is any).
*/ */
@ServiceProvider(service = DataContentViewer.class, position=4) @ServiceProvider(service = DataContentViewer.class, position = 4)
public class ExtractedContentViewer implements DataContentViewer { public class ExtractedContentViewer implements DataContentViewer {
private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName()); private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName());
@ -53,7 +53,6 @@ public class ExtractedContentViewer implements DataContentViewer {
private Node currentNode = null; private Node currentNode = null;
private MarkupSource currentSource = null; private MarkupSource currentSource = null;
private final IsDirVisitor isDirVisitor = new IsDirVisitor(); private final IsDirVisitor isDirVisitor = new IsDirVisitor();
//keep last content cached //keep last content cached
private String curContent; private String curContent;
private long curContentId; private long curContentId;
@ -77,12 +76,18 @@ public class ExtractedContentViewer implements DataContentViewer {
// sources are custom markup from the node (if available) and default // sources are custom markup from the node (if available) and default
// markup is fetched from solr // markup is fetched from solr
List<MarkupSource> sources = new ArrayList<MarkupSource>(); final List<MarkupSource> sources = new ArrayList<MarkupSource>();
//add additional registered sources for this node //add additional registered sources for this node
sources.addAll(selectedNode.getLookup().lookupAll(MarkupSource.class)); sources.addAll(selectedNode.getLookup().lookupAll(MarkupSource.class));
if (solrHasContent(selectedNode)) { if (!solrHasContent(selectedNode)) {
//currentNode = null;
//resetComponent();
// first source will be the default displayed
setPanel(sources);
return;
}
Content content = selectedNode.getLookup().lookup(Content.class); Content content = selectedNode.getLookup().lookup(Content.class);
if (content == null) { if (content == null) {
return; return;
@ -91,8 +96,7 @@ public class ExtractedContentViewer implements DataContentViewer {
//add to page tracking if not there yet //add to page tracking if not there yet
final long contentID = content.getId(); final long contentID = content.getId();
MarkupSource newSource = new MarkupSource() { final MarkupSource newSource = new MarkupSource() {
private boolean inited = false; private boolean inited = false;
private int numPages = 0; private int numPages = 0;
private int currentPage = 0; private int currentPage = 0;
@ -231,7 +235,7 @@ public class ExtractedContentViewer implements DataContentViewer {
updatePageControls(); updatePageControls();
}
// first source will be the default displayed // first source will be the default displayed
@ -248,7 +252,6 @@ public class ExtractedContentViewer implements DataContentViewer {
// using invokeLater to wait for ComboBox selection to complete // using invokeLater to wait for ComboBox selection to complete
EventQueue.invokeLater(new Runnable() { EventQueue.invokeLater(new Runnable() {
@Override @Override
public void run() { public void run() {
panel.scrollToAnchor(source.getAnchorPrefix() + Integer.toString(source.currentItem())); panel.scrollToAnchor(source.getAnchorPrefix() + Integer.toString(source.currentItem()));
@ -306,10 +309,10 @@ public class ExtractedContentViewer implements DataContentViewer {
public int isPreferred(Node node, public int isPreferred(Node node,
boolean isSupported) { boolean isSupported) {
BlackboardArtifact art = node.getLookup().lookup(BlackboardArtifact.class); BlackboardArtifact art = node.getLookup().lookup(BlackboardArtifact.class);
if(isSupported) { if (isSupported) {
if(art == null) { if (art == null) {
return 4; return 4;
} else if(art.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { } else if (art.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
return 6; return 6;
} else { } else {
return 4; return 4;
@ -322,6 +325,7 @@ public class ExtractedContentViewer implements DataContentViewer {
/** /**
* Set the MarkupSources for the panel to display (safe to call even if the * Set the MarkupSources for the panel to display (safe to call even if the
* panel hasn't been created yet) * panel hasn't been created yet)
*
* @param sources * @param sources
*/ */
private void setPanel(List<MarkupSource> sources) { private void setPanel(List<MarkupSource> sources) {
@ -355,6 +359,7 @@ public class ExtractedContentViewer implements DataContentViewer {
/** /**
* Check if Solr has extracted content for a given node * Check if Solr has extracted content for a given node
*
* @param node * @param node
* @return true if Solr has content, else false * @return true if Solr has content, else false
*/ */
@ -364,8 +369,9 @@ public class ExtractedContentViewer implements DataContentViewer {
return false; return false;
} }
if (content.getSize() == 0) if (content.getSize() == 0) {
return false; return false;
}
final Server solrServer = KeywordSearch.getServer(); final Server solrServer = KeywordSearch.getServer();
@ -389,10 +395,12 @@ public class ExtractedContentViewer implements DataContentViewer {
/** /**
* Get extracted content for a node from Solr * Get extracted content for a node from Solr
*
* @param node a node that has extracted content in Solr (check with * @param node a node that has extracted content in Solr (check with
* solrHasContent(ContentNode)) * solrHasContent(ContentNode))
* @param currentPage currently used page * @param currentPage currently used page
* @param hasChunks true if the content behind the node has multiple chunks. This means we need to address the content pages specially. * @param hasChunks true if the content behind the node has multiple chunks.
* This means we need to address the content pages specially.
* @return the extracted content * @return the extracted content
* @throws SolrServerException if something goes wrong * @throws SolrServerException if something goes wrong
*/ */