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.ItemListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.JMenuItem;
import javax.swing.SwingWorker;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.Element;
@ -36,29 +36,30 @@ import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
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
* a combo-box to select between multiple sources.
* Panel displays HTML content sent to ExtractedContentViewer, and provides a
* combo-box to select between multiple sources.
*/
class ExtractedContentPanel extends javax.swing.JPanel {
private static Logger logger = Logger.getLogger(ExtractedContentPanel.class.getName());
ExtractedContentPanel() {
ExtractedContentPanel() {
initComponents();
initControls();
customizeComponents();
}
private void customizeComponents() {
extractedTextPane.setEditorKit(new HTMLEditorKit() {
ViewFactory viewFactory = new HTMLFactory() {
@Override
public View create(Element elem) {
AttributeSet attrs = elem.getAttributes();
@ -81,38 +82,39 @@ class ExtractedContentPanel extends javax.swing.JPanel {
});
sourceComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
MarkupSource source = (MarkupSource) e.getItem();
setPanelText(source.getMarkup());
//setPanelText(source.getMarkup());
new SetMarkup(source).execute();
}
}
});
setSources(new ArrayList<MarkupSource>());
extractedTextPane.setComponentPopupMenu(rightClickMenu);
ActionListener actList = new ActionListener(){
ActionListener actList = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e){
public void actionPerformed(ActionEvent e) {
JMenuItem jmi = (JMenuItem) e.getSource();
if(jmi.equals(copyMenuItem))
if (jmi.equals(copyMenuItem)) {
extractedTextPane.copy();
else if(jmi.equals(selectAllMenuItem))
} else if (jmi.equals(selectAllMenuItem)) {
extractedTextPane.selectAll();
}
}
};
copyMenuItem.addActionListener(actList);
selectAllMenuItem.addActionListener(actList);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <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))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JMenuItem copyMenuItem;
private javax.swing.JTextPane extractedTextPane;
@ -306,16 +307,17 @@ class ExtractedContentPanel extends javax.swing.JPanel {
private javax.swing.JComboBox sourceComboBox;
// End of variables declaration//GEN-END:variables
void refreshCurrentMarkup() {
MarkupSource ms = (MarkupSource)sourceComboBox.getSelectedItem();
setPanelText(ms.getMarkup());
MarkupSource ms = (MarkupSource) sourceComboBox.getSelectedItem();
//setPanelText(ms.getMarkup());
new SetMarkup(ms).execute();
}
/**
* Set the available sources (selects the first source in the list by
* default)
* @param sources
*
* @param sources
*/
void setSources(List<MarkupSource> sources) {
sourceComboBox.removeAllItems();
@ -331,7 +333,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
}
/**
*
*
* @return currently available sources on the panel
*/
public List<MarkupSource> getSources() {
@ -343,7 +345,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
}
/**
*
*
* @return currently selected Source
*/
public MarkupSource getSelectedSource() {
@ -360,34 +362,36 @@ class ExtractedContentPanel extends javax.swing.JPanel {
hitNextButton.setEnabled(false);
}
void scrollToAnchor(String anchor) {
extractedTextPane.scrollToReference(anchor);
}
/**
*
*
* @param current, current hit to update the display with
*/
void updateCurrentMatchDisplay(int current) {
if (current == 0)
if (current == 0) {
hitCountLabel.setText("-");
else hitCountLabel.setText(Integer.toString(current));
} else {
hitCountLabel.setText(Integer.toString(current));
}
}
/**
*
*
* @param total total number of hits to update the display with
*/
void updateTotaMatcheslDisplay(int total) {
if (total == 0)
if (total == 0) {
hitTotalLabel.setText("-");
else hitTotalLabel.setText(Integer.toString(total));
} else {
hitTotalLabel.setText(Integer.toString(total));
}
}
/**
*
*
* @param current, current page to update the display with
*/
void updateCurrentPageDisplay(int current) {
@ -395,19 +399,18 @@ class ExtractedContentPanel extends javax.swing.JPanel {
}
/**
*
*
* @param total total number of pages to update the display with
*/
void updateTotalPageslDisplay(int total) {
pageTotalLabel.setText(Integer.toString(total));
}
void resetDisplay() {
resetHitDisplay();
resetPagesDisplay();
}
/**
* reset the current/total hits display
*/
@ -415,7 +418,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
hitTotalLabel.setText("-");
hitCountLabel.setText("-");
}
/**
* reset the current/total pages display
*/
@ -426,6 +429,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
/**
* enable previous match control
*
* @param enable whether to enable or disable
*/
void enablePrevMatchControl(boolean enable) {
@ -434,6 +438,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
/**
* enable next match control
*
* @param enable whether to enable or disable
*/
void enableNextMatchControl(boolean enable) {
@ -447,10 +452,10 @@ class ExtractedContentPanel extends javax.swing.JPanel {
void addNextMatchControlListener(ActionListener l) {
hitNextButton.addActionListener(l);
}
/**
* enable previous oage control
*
* @param enable whether to enable or disable
*/
void enablePrevPageControl(boolean enable) {
@ -459,6 +464,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
/**
* enable next page control
*
* @param enable whether to enable or disable
*/
void enableNextPageControl(boolean enable) {
@ -476,4 +482,43 @@ class ExtractedContentPanel extends javax.swing.JPanel {
void addSourceComboControlListener(ActionListener 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

@ -41,11 +41,11 @@ import org.sleuthkit.datamodel.ContentVisitor;
import org.sleuthkit.datamodel.Directory;
/**
* 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
* Solr extracted (if there is any).
* 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 Solr
* extracted (if there is any).
*/
@ServiceProvider(service = DataContentViewer.class, position=4)
@ServiceProvider(service = DataContentViewer.class, position = 4)
public class ExtractedContentViewer implements DataContentViewer {
private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName());
@ -53,7 +53,6 @@ public class ExtractedContentViewer implements DataContentViewer {
private Node currentNode = null;
private MarkupSource currentSource = null;
private final IsDirVisitor isDirVisitor = new IsDirVisitor();
//keep last content cached
private String curContent;
private long curContentId;
@ -77,163 +76,168 @@ public class ExtractedContentViewer implements DataContentViewer {
// sources are custom markup from the node (if available) and default
// 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
sources.addAll(selectedNode.getLookup().lookupAll(MarkupSource.class));
if (solrHasContent(selectedNode)) {
Content content = selectedNode.getLookup().lookup(Content.class);
if (content == null) {
return;
if (!solrHasContent(selectedNode)) {
//currentNode = null;
//resetComponent();
// first source will be the default displayed
setPanel(sources);
return;
}
Content content = selectedNode.getLookup().lookup(Content.class);
if (content == null) {
return;
}
//add to page tracking if not there yet
final long contentID = content.getId();
final MarkupSource newSource = new MarkupSource() {
private boolean inited = false;
private int numPages = 0;
private int currentPage = 0;
private boolean hasChunks = false;
@Override
public int getCurrentPage() {
return this.currentPage;
}
//add to page tracking if not there yet
final long contentID = content.getId();
@Override
public boolean hasNextPage() {
return currentPage < numPages;
}
MarkupSource newSource = new MarkupSource() {
@Override
public boolean hasPreviousPage() {
return currentPage > 1;
}
private boolean inited = false;
private int numPages = 0;
private int currentPage = 0;
private boolean hasChunks = false;
@Override
public int getCurrentPage() {
return this.currentPage;
@Override
public int nextPage() {
if (!hasNextPage()) {
throw new IllegalStateException("No next page.");
}
++currentPage;
return currentPage;
}
@Override
public boolean hasNextPage() {
return currentPage < numPages;
@Override
public int previousPage() {
if (!hasPreviousPage()) {
throw new IllegalStateException("No previous page.");
}
--currentPage;
return currentPage;
}
@Override
public boolean hasPreviousPage() {
return currentPage > 1;
}
@Override
public boolean hasNextItem() {
throw new UnsupportedOperationException("Not supported, not a searchable source.");
}
@Override
public int nextPage() {
if (!hasNextPage()) {
throw new IllegalStateException("No next page.");
}
++currentPage;
return currentPage;
}
@Override
public boolean hasPreviousItem() {
throw new UnsupportedOperationException("Not supported, not a searchable source.");
}
@Override
public int previousPage() {
if (!hasPreviousPage()) {
throw new IllegalStateException("No previous page.");
}
--currentPage;
return currentPage;
}
@Override
public int nextItem() {
throw new UnsupportedOperationException("Not supported, not a searchable source.");
}
@Override
public boolean hasNextItem() {
throw new UnsupportedOperationException("Not supported, not a searchable source.");
}
@Override
public int previousItem() {
throw new UnsupportedOperationException("Not supported, not a searchable source.");
}
@Override
public boolean hasPreviousItem() {
throw new UnsupportedOperationException("Not supported, not a searchable source.");
}
@Override
public int currentItem() {
throw new UnsupportedOperationException("Not supported, not a searchable source.");
}
@Override
public int nextItem() {
throw new UnsupportedOperationException("Not supported, not a searchable source.");
}
@Override
public int previousItem() {
throw new UnsupportedOperationException("Not supported, not a searchable source.");
}
@Override
public int currentItem() {
throw new UnsupportedOperationException("Not supported, not a searchable source.");
}
@Override
public String getMarkup() {
try {
return getSolrContent(selectedNode, currentPage, hasChunks);
} catch (SolrServerException ex) {
logger.log(Level.WARNING, "Couldn't get extracted content.", ex);
return "";
}
}
@Override
public String toString() {
return "Extracted Content";
}
@Override
public boolean isSearchable() {
return false;
}
@Override
public String getAnchorPrefix() {
@Override
public String getMarkup() {
try {
return getSolrContent(selectedNode, currentPage, hasChunks);
} catch (SolrServerException ex) {
logger.log(Level.WARNING, "Couldn't get extracted content.", ex);
return "";
}
@Override
public int getNumberHits() {
return 0;
}
@Override
public LinkedHashMap<Integer, Integer> getHitsPages() {
return null;
}
@Override
public int getNumberPages() {
if (inited) {
return this.numPages;
}
final Server solrServer = KeywordSearch.getServer();
try {
numPages = solrServer.queryNumFileChunks(contentID);
if (numPages == 0) {
numPages = 1;
hasChunks = false;
} else {
hasChunks = true;
}
inited = true;
} catch (KeywordSearchModuleException ex) {
logger.log(Level.WARNING, "Could not get number of chunks: ", ex);
} catch (NoOpenCoreException ex) {
logger.log(Level.WARNING, "Could not get number of chunks: ", ex);
}
return numPages;
}
};
currentSource = newSource;
sources.add(newSource);
//init pages
final int totalPages = currentSource.getNumberPages();
int currentPage = currentSource.getCurrentPage();
if (currentPage == 0 && currentSource.hasNextPage()) {
currentSource.nextPage();
}
@Override
public String toString() {
return "Extracted Content";
}
updatePageControls();
@Override
public boolean isSearchable() {
return false;
}
@Override
public String getAnchorPrefix() {
return "";
}
@Override
public int getNumberHits() {
return 0;
}
@Override
public LinkedHashMap<Integer, Integer> getHitsPages() {
return null;
}
@Override
public int getNumberPages() {
if (inited) {
return this.numPages;
}
final Server solrServer = KeywordSearch.getServer();
try {
numPages = solrServer.queryNumFileChunks(contentID);
if (numPages == 0) {
numPages = 1;
hasChunks = false;
} else {
hasChunks = true;
}
inited = true;
} catch (KeywordSearchModuleException ex) {
logger.log(Level.WARNING, "Could not get number of chunks: ", ex);
} catch (NoOpenCoreException ex) {
logger.log(Level.WARNING, "Could not get number of chunks: ", ex);
}
return numPages;
}
};
currentSource = newSource;
sources.add(newSource);
//init pages
final int totalPages = currentSource.getNumberPages();
int currentPage = currentSource.getCurrentPage();
if (currentPage == 0 && currentSource.hasNextPage()) {
currentSource.nextPage();
}
updatePageControls();
// first source will be the default displayed
setPanel(sources);
// If node has been selected before, return to the previous position
@ -248,7 +252,6 @@ public class ExtractedContentViewer implements DataContentViewer {
// using invokeLater to wait for ComboBox selection to complete
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
panel.scrollToAnchor(source.getAnchorPrefix() + Integer.toString(source.currentItem()));
@ -306,10 +309,10 @@ public class ExtractedContentViewer implements DataContentViewer {
public int isPreferred(Node node,
boolean isSupported) {
BlackboardArtifact art = node.getLookup().lookup(BlackboardArtifact.class);
if(isSupported) {
if(art == null) {
if (isSupported) {
if (art == null) {
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;
} else {
return 4;
@ -322,7 +325,8 @@ public class ExtractedContentViewer implements DataContentViewer {
/**
* Set the MarkupSources for the panel to display (safe to call even if the
* panel hasn't been created yet)
* @param sources
*
* @param sources
*/
private void setPanel(List<MarkupSource> sources) {
if (panel != null) {
@ -355,6 +359,7 @@ public class ExtractedContentViewer implements DataContentViewer {
/**
* Check if Solr has extracted content for a given node
*
* @param node
* @return true if Solr has content, else false
*/
@ -364,8 +369,9 @@ public class ExtractedContentViewer implements DataContentViewer {
return false;
}
if (content.getSize() == 0)
if (content.getSize() == 0) {
return false;
}
final Server solrServer = KeywordSearch.getServer();
@ -389,10 +395,12 @@ public class ExtractedContentViewer implements DataContentViewer {
/**
* Get extracted content for a node from Solr
*
* @param node a node that has extracted content in Solr (check with
* solrHasContent(ContentNode))
* @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 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.
* @return the extracted content
* @throws SolrServerException if something goes wrong
*/