diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index f22f381760..d55aaa4561 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -3,3 +3,7 @@ IndexProgressPanel.statusText.text=Status text IndexProgressPanel.cancelButton.text=Cancel KeywordSearchTopComponent.searchButton.text=Search KeywordSearchTopComponent.queryLabel.text=Solr query: +KeywordSearchTopComponent.filesIndexedNameLabel.text=Files indexed: +KeywordSearchTopComponent.filesIndexedValLabel.text=- +KeywordSearchTopComponent.filesIndexedNameLabel.AccessibleContext.accessibleName=Files indexed: +KeywordSearchTopComponent.filesIndexedValLabel.AccessibleContext.accessibleName=- diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexContentFilesAction.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexContentFilesAction.java index ab9645f61a..8582abc972 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexContentFilesAction.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexContentFilesAction.java @@ -35,6 +35,7 @@ import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.SwingWorker; +import org.apache.solr.client.solrj.SolrServerException; import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.FsContent; @@ -44,13 +45,12 @@ import org.sleuthkit.datamodel.FsContent; * children to the Solr index. */ public class IndexContentFilesAction extends AbstractAction { - + private static final Logger logger = Logger.getLogger(IndexContentFilesAction.class.getName()); - private Content c; private String name; private Server.Core solrCore; - + /** * New action * @param c source Content object to get files from @@ -59,7 +59,7 @@ public class IndexContentFilesAction extends AbstractAction { public IndexContentFilesAction(Content c, String name) { this(c, name, KeywordSearch.getServer().getCore()); } - + IndexContentFilesAction(Content c, String name, Server.Core solrCore) { super("Index files..."); this.c = c; @@ -69,7 +69,7 @@ public class IndexContentFilesAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { - + // create the popUp window to display progress String title = "Indexing files in " + name; @@ -93,7 +93,7 @@ public class IndexContentFilesAction extends AbstractAction { int fileCount = files.size(); int finishedFiles = 0; int problemFiles = 0; - + for (FsContent f : files) { if (isCancelled()) { return problemFiles; @@ -113,13 +113,20 @@ public class IndexContentFilesAction extends AbstractAction { ingester.commit(); + try { + final int numIndexedFiles = KeywordSearch.getServer().getCore().queryNumIndexedFiles(); + KeywordSearch.changeSupport.firePropertyChange(KeywordSearch.NUM_FILES_CHANGE_EVT, null, new Integer(numIndexedFiles)); + } catch (SolrServerException se) { + logger.log(Level.SEVERE, "Error executing Solr query, " + se.getMessage()); + } + return problemFiles; } @Override protected void done() { int problemFiles = 0; - + try { if (!this.isCancelled()) { problemFiles = get(); @@ -133,7 +140,7 @@ public class IndexContentFilesAction extends AbstractAction { } finally { popUpWindow.setVisible(false); popUpWindow.dispose(); - + // notify user if there were problem files if (problemFiles > 0) { displayProblemFilesDialog(problemFiles); @@ -187,8 +194,7 @@ public class IndexContentFilesAction extends AbstractAction { // display the window popUpWindow.setVisible(true); } - - + private void displayProblemFilesDialog(int problemFiles) { final Component parentComponent = null; // Use default window frame. final String message = "Had trouble indexing " + problemFiles + " of the files. See the log for details."; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearch.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearch.java index 23dab824dc..198c9359a5 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearch.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearch.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.keywordsearch; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import org.sleuthkit.autopsy.casemodule.Case; /** @@ -30,6 +31,10 @@ class KeywordSearch { private static final String BASE_URL = "http://localhost:8983/solr/"; private static final Server SERVER = new Server(BASE_URL); + public static final String NUM_FILES_CHANGE_EVT = "NUM_FILES_CHANGE_EVT"; + + static PropertyChangeSupport changeSupport = new PropertyChangeSupport(KeywordSearch.class); + static Server getServer() { return SERVER; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchDataExplorer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchDataExplorer.java index ba2d16e9ae..cbbfd2b5f0 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchDataExplorer.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchDataExplorer.java @@ -22,6 +22,7 @@ import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -64,6 +65,8 @@ public class KeywordSearchDataExplorer implements DataExplorer { } } }); + + KeywordSearch.changeSupport.addPropertyChangeListener(KeywordSearch.NUM_FILES_CHANGE_EVT, new IndexChangeListener()); } private synchronized void setTheInstance() { @@ -139,4 +142,28 @@ public class KeywordSearchDataExplorer implements DataExplorer { @Override public void propertyChange(PropertyChangeEvent evt) { } + + + + class IndexChangeListener implements PropertyChangeListener { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + + String changed = evt.getPropertyName(); + //Object oldValue = evt.getOldValue(); + Object newValue = evt.getNewValue(); + + if (newValue != null) { + if (changed.equals(KeywordSearch.NUM_FILES_CHANGE_EVT)) { + int newFilesIndexed = ((Integer) newValue).intValue(); + tc.setFilesIndexed(newFilesIndexed); + + } else { + String msg = "Unsupported change event: " + changed; + throw new UnsupportedOperationException(msg); +} + } + } + } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTopComponent.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTopComponent.form index 58ea055eb3..8033d2ee32 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTopComponent.form +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTopComponent.form @@ -20,8 +20,13 @@ - + + + + + + @@ -36,7 +41,12 @@ - + + + + + + @@ -71,5 +81,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTopComponent.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTopComponent.java index a3f42407b2..a97ca82084 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTopComponent.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTopComponent.java @@ -19,16 +19,30 @@ package org.sleuthkit.autopsy.keywordsearch; import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.solr.client.solrj.SolrServerException; import org.openide.windows.TopComponent; public class KeywordSearchTopComponent extends TopComponent { + private Logger logger = Logger.getLogger(KeywordSearchTopComponent.class.getName()); + private PropertyChangeListener serverChangeListener; + /** Creates new form KeywordSearchTopComponent */ public KeywordSearchTopComponent() { initComponents(); setName("Keyword Search"); + searchButton.setEnabled(false); + putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE); + + //register with server Actions + serverChangeListener = new KeywordSearchServerListener(); + KeywordSearch.getServer().addServerActionListener(serverChangeListener); } /** This method is called from within the constructor to @@ -44,6 +58,8 @@ public class KeywordSearchTopComponent extends TopComponent { queryTextArea = new javax.swing.JTextArea(); searchButton = new javax.swing.JButton(); queryLabel = new javax.swing.JLabel(); + filesIndexedNameLabel = new javax.swing.JLabel(); + filesIndexedValLabel = new javax.swing.JLabel(); queryTextArea.setColumns(20); queryTextArea.setRows(5); @@ -53,21 +69,24 @@ public class KeywordSearchTopComponent extends TopComponent { queryLabel.setText(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.queryLabel.text")); // NOI18N + filesIndexedNameLabel.setText(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.filesIndexedNameLabel.text")); // NOI18N + + filesIndexedValLabel.setText(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.filesIndexedValLabel.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() + .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(queryLabel) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 599, Short.MAX_VALUE) + .addComponent(searchButton) .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(queryLabel)) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 302, Short.MAX_VALUE)) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(searchButton))) + .addComponent(filesIndexedNameLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(filesIndexedValLabel))) .addContainerGap()) ); layout.setVerticalGroup( @@ -79,10 +98,19 @@ public class KeywordSearchTopComponent extends TopComponent { .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) .addComponent(searchButton) - .addContainerGap(132, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(filesIndexedNameLabel) + .addComponent(filesIndexedValLabel)) + .addContainerGap(107, Short.MAX_VALUE)) ); + + filesIndexedNameLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.filesIndexedNameLabel.AccessibleContext.accessibleName")); // NOI18N + filesIndexedValLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.filesIndexedValLabel.AccessibleContext.accessibleName")); // NOI18N }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel filesIndexedNameLabel; + private javax.swing.JLabel filesIndexedValLabel; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JLabel queryLabel; private javax.swing.JTextArea queryTextArea; @@ -96,7 +124,7 @@ public class KeywordSearchTopComponent extends TopComponent { String getQueryText() { return queryTextArea.getText(); } - + /** * Overwrite when you want to change default persistence type. Default * persistence type is PERSISTENCE_ALWAYS @@ -107,4 +135,43 @@ public class KeywordSearchTopComponent extends TopComponent { public int getPersistenceType() { return TopComponent.PERSISTENCE_NEVER; } + + public void setFilesIndexed(int filesIndexed) { + filesIndexedValLabel.setText(Integer.toString(filesIndexed)); + if (filesIndexed == 0) { + searchButton.setEnabled(false); + } else { + searchButton.setEnabled(true); + } + } + + + class KeywordSearchServerListener implements PropertyChangeListener { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + + if (eventType.equals(Server.CORE_EVT)) { + final Server.CORE_EVT_STATES state = (Server.CORE_EVT_STATES) evt.getNewValue(); + switch (state) { + case STARTED: + try { + final int numIndexedFiles = KeywordSearch.getServer().getCore().queryNumIndexedFiles(); + KeywordSearch.changeSupport.firePropertyChange(KeywordSearch.NUM_FILES_CHANGE_EVT, null, new Integer(numIndexedFiles)); + } catch (SolrServerException se) { + logger.log(Level.SEVERE, "Error executing Solr query, " + se.getMessage()); + } + break; + case STOPPED: + break; + default: + + } + + } + + + } + } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 9108a52066..b97d4521d6 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.keywordsearch; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeListener; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -28,6 +30,7 @@ import java.net.MalformedURLException; import java.net.SocketException; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.AbstractAction; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServer; import org.apache.solr.client.solrj.SolrServerException; @@ -47,10 +50,13 @@ class Server { private static final String DEFAULT_CORE_NAME = "coreCase"; // TODO: DEFAULT_CORE_NAME needs to be replaced with unique names to support multiple open cases + public static final String CORE_EVT = "CORE_EVT"; + public enum CORE_EVT_STATES { STOPPED, STARTED }; private CommonsHttpSolrServer solr; private String instanceDir; private File solrFolder; + private ServerAction serverAction; /** * New instance for the server at the given URL @@ -63,10 +69,14 @@ class Server { throw new RuntimeException(ex); } + serverAction = new ServerAction(); solrFolder = InstalledFileLocator.getDefault().locate("solr", Server.class.getPackage().getName(), false); instanceDir = solrFolder.getAbsolutePath() + File.separator + "solr"; } + public void addServerActionListener(PropertyChangeListener l) { + serverAction.addPropertyChangeListener(l); + } /** * Helper threads to handle stderr/stdout from Solr process @@ -79,6 +89,7 @@ class Server { this.stream = stream; } + @Override public void run() { InputStreamReader isr = new InputStreamReader(stream); BufferedReader br = new BufferedReader(isr); @@ -174,6 +185,7 @@ class Server { throw new RuntimeException("Already an open Core!"); } currentCore = openCore(Case.getCurrentCase()); + serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STARTED); } void closeCore() { @@ -182,6 +194,7 @@ class Server { } currentCore.close(); currentCore = null; + serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STOPPED); } Core getCore() { @@ -269,5 +282,26 @@ class Server { throw new RuntimeException(ex); } } + + /** + * Execute query that gets only number of all Solr documents indexed + * without actually returning the documents + * @return int representing number of indexed files + * @throws SolrServerException + */ + public int queryNumIndexedFiles() throws SolrServerException { + SolrQuery q = new SolrQuery("*:*"); + q.setRows(0); + return (int)query(q).getResults().getNumFound(); + } +} + + class ServerAction extends AbstractAction { + + @Override + public void actionPerformed(ActionEvent e) { + logger.log(Level.INFO, e.paramString()); + } + } }