diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java index 8e6d6a6b6e..9804d06ca9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java @@ -104,17 +104,18 @@ public abstract class AbstractCommonAttributeSearcher { } } - static Map> collateMatchesByNumberOfInstances(Map commonFiles) { + static Map collateMatchesByNumberOfInstances(Map commonFiles) { //collate matches by number of matching instances - doing this in sql doesnt seem efficient - Map> instanceCollatedCommonFiles = new TreeMap<>(); + Map instanceCollatedCommonFiles = new TreeMap<>(); + for(CommonAttributeValue md5Metadata : commonFiles.values()){ Integer size = md5Metadata.getInstanceCount(); if(instanceCollatedCommonFiles.containsKey(size)){ - instanceCollatedCommonFiles.get(size).add(md5Metadata); + instanceCollatedCommonFiles.get(size).addMetadataToList(md5Metadata); } else { - ArrayList value = new ArrayList<>(); - value.add(md5Metadata); + CommonAttributeValueList value = new CommonAttributeValueList(); + value.addMetadataToList(md5Metadata); instanceCollatedCommonFiles.put(size, value); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java index 1af231f496..7e434f3db0 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java @@ -49,7 +49,7 @@ public class AllInterCaseCommonAttributeSearcher extends InterCaseCommonAttribut @Override public CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap(), corAttrType); - Map> interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase()); + Map interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase()); return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties index 2b9d98b2ab..fb0e22ba54 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties @@ -9,10 +9,14 @@ IntraCasePanel.withinDataSourceRadioButton.text=At least one match must appear i IntraCasePanel.selectDataSourceComboBox.actionCommand= InterCasePanel.specificCentralRepoCaseRadio.text=Matches must be from the following Central Repo case: InterCasePanel.anyCentralRepoCaseRadio.text=Matches may be from any Central Repo case -CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search +CommonAttributePanel.jCheckBox1.text=Hide files found in over +CommonAttributePanel.jLabel1.text=% of data sources in central repository. +CommonAttributePanel.percentageThresholdTextTwo.text_1=% of data sources in central repository. +CommonAttributePanel.percentageThresholdTextOne.text=20 +CommonAttributePanel.percentageThresholdCheck.text_1=Hide files found in over CommonAttributePanel.intraCaseRadio.text=Within current case CommonAttributePanel.commonFilesSearchLabel1.text=Find common files to correlate data soures or cases. -CommonAttributePanel.errorText.text=In order to search, you must select a file category. +CommonAttributePanel.errorText.text=In order to search, you must select a file category. CommonAttributePanel.categoriesLabel.text=File Types To Include: CommonAttributePanel.documentsCheckbox.text=Documents CommonAttributePanel.pictureVideoCheckbox.text=Pictures and Videos @@ -30,3 +34,4 @@ CommonAttributePanel.jLabel1.text_1=% of data sources in central repository. CommonAttributePanel.percentageThresholdCheck.text_1=Hide files found in over InterCasePanel.comboBoxLabel.text=Select correlation type to search: InterCasePanel.correlationTypeComboBox.toolTipText=Selected Correlation Type +CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index 4eb7f8ae1c..3f628f61a3 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -9,10 +9,10 @@ - + - + @@ -40,14 +40,15 @@ - + - + - + + @@ -58,45 +59,57 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -126,16 +139,22 @@ - - + + - - - - + + + + + + + + + + - + @@ -251,6 +270,7 @@ + @@ -318,10 +338,10 @@ - + - + @@ -334,13 +354,33 @@ - + - + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 9e3dc79d69..071fb8b9b5 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -85,7 +85,6 @@ public final class CommonAttributePanel extends javax.swing.JDialog { initComponents(); this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - this.errorText.setVisible(false); this.setupDataSources(); if (CommonAttributePanel.isEamDbAvailableForIntercaseSearch()) { @@ -102,13 +101,13 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } this.errorManager = new UserInputErrorManager(); - - this.percentageThreshold.getDocument().addDocumentListener(new DocumentListener() { - - private Dimension preferredSize = CommonAttributePanel.this.percentageThreshold.getPreferredSize(); + + this.percentageThresholdTextOne.getDocument().addDocumentListener(new DocumentListener(){ + + private Dimension preferredSize = CommonAttributePanel.this.percentageThresholdTextOne.getPreferredSize(); private void maintainSize() { - CommonAttributePanel.this.percentageThreshold.setSize(preferredSize); + CommonAttributePanel.this.percentageThresholdTextOne.setSize(preferredSize); } @Override @@ -472,11 +471,13 @@ public final class CommonAttributePanel extends javax.swing.JDialog { intraCasePanel = new org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel(); interCasePanel = new org.sleuthkit.autopsy.commonfilesearch.InterCasePanel(); percentageThresholdCheck = new javax.swing.JCheckBox(); - percentageThreshold = new javax.swing.JTextField(); - jLabel1 = new javax.swing.JLabel(); + percentageThresholdTextOne = new javax.swing.JTextField(); + percentageThresholdTextTwo = new javax.swing.JLabel(); + filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); + filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 32767)); - setMaximumSize(new java.awt.Dimension(412, 440)); - setMinimumSize(new java.awt.Dimension(412, 440)); + setMaximumSize(new java.awt.Dimension(450, 440)); + setMinimumSize(new java.awt.Dimension(450, 440)); setResizable(false); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosed(java.awt.event.WindowEvent evt) { @@ -484,9 +485,10 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } }); - jPanel1.setMaximumSize(new java.awt.Dimension(412, 440)); - jPanel1.setMinimumSize(new java.awt.Dimension(412, 440)); - jPanel1.setPreferredSize(new java.awt.Dimension(412, 440)); + jPanel1.setMaximumSize(new java.awt.Dimension(450, 440)); + jPanel1.setMinimumSize(new java.awt.Dimension(450, 440)); + jPanel1.setPreferredSize(new java.awt.Dimension(450, 375)); + jPanel1.setRequestFocusEnabled(false); org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel2, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel2.text")); // NOI18N commonFilesSearchLabel2.setFocusable(false); @@ -554,6 +556,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { errorText.setForeground(new java.awt.Color(255, 0, 0)); org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.errorText.text")); // NOI18N + errorText.setVerticalAlignment(javax.swing.SwingConstants.TOP); org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel1.text")); // NOI18N commonFilesSearchLabel1.setFocusable(false); @@ -586,12 +589,12 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } }); - percentageThreshold.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThreshold.text")); // NOI18N - percentageThreshold.setMaximumSize(new java.awt.Dimension(40, 24)); - percentageThreshold.setMinimumSize(new java.awt.Dimension(40, 24)); - percentageThreshold.setPreferredSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdTextOne.text")); // NOI18N + percentageThresholdTextOne.setMaximumSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setMinimumSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setPreferredSize(new java.awt.Dimension(40, 24)); - org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.jLabel1.text_1")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(percentageThresholdTextTwo, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdTextTwo.text_1")); // NOI18N javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); @@ -600,35 +603,43 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(commonFilesSearchLabel2) - .addComponent(intraCaseRadio) - .addComponent(interCaseRadio) - .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(categoriesLabel) - .addComponent(selectedFileCategoriesButton) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(29, 29, 29) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(documentsCheckbox) - .addComponent(pictureVideoCheckbox))) - .addComponent(allFileCategoriesRadioButton) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() - .addGap(10, 10, 10) - .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(percentageThresholdCheck) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(percentageThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel1)) - .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addComponent(searchButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cancelButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(errorText))) - .addContainerGap()) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(80, 80, 80) + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(errorText))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(commonFilesSearchLabel2) + .addComponent(intraCaseRadio) + .addComponent(interCaseRadio) + .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(categoriesLabel) + .addComponent(selectedFileCategoriesButton) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(29, 29, 29) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(documentsCheckbox) + .addComponent(pictureVideoCheckbox))) + .addComponent(allFileCategoriesRadioButton) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addGap(10, 10, 10) + .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(percentageThresholdCheck) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(percentageThresholdTextTwo))) + .addContainerGap(9, Short.MAX_VALUE)))) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -656,68 +667,72 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(percentageThresholdCheck) - .addComponent(percentageThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel1)) + .addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(percentageThresholdTextTwo)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(searchButton) - .addComponent(cancelButton) - .addComponent(errorText)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(searchButton) + .addComponent(cancelButton) + .addComponent(errorText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) ); getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents - private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed - search(); - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_searchButtonActionPerformed - - private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_cancelButtonActionPerformed - - private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed - - private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed - - private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_pictureVideoCheckboxActionPerformed - - private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_documentsCheckboxActionPerformed - - private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed - ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); - }//GEN-LAST:event_intraCaseRadioActionPerformed - - private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed - ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); - }//GEN-LAST:event_interCaseRadioActionPerformed - private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed SwingUtilities.windowForComponent(this).dispose(); }//GEN-LAST:event_formWindowClosed private void percentageThresholdCheckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdCheckActionPerformed if (this.percentageThresholdCheck.isSelected()) { - this.percentageThreshold.setEnabled(true); + this.percentageThresholdTextOne.setEnabled(true); } else { - this.percentageThreshold.setEnabled(false); + this.percentageThresholdTextOne.setEnabled(false); } this.handleFrequencyPercentageState(); }//GEN-LAST:event_percentageThresholdCheckActionPerformed + private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed + ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); + }//GEN-LAST:event_interCaseRadioActionPerformed + + private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed + ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); + }//GEN-LAST:event_intraCaseRadioActionPerformed + + private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_documentsCheckboxActionPerformed + + private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_pictureVideoCheckboxActionPerformed + + private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed + + private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed + search(); + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_searchButtonActionPerformed + private void percentageThresholdChanged() { - String percentageString = this.percentageThreshold.getText(); + String percentageString = this.percentageThresholdTextOne.getText(); try { this.percentageThresholdValue = Integer.parseInt(percentageString); @@ -743,15 +758,17 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } private void enablePercentageOptions() { - this.percentageThreshold.setEnabled(true); + this.percentageThresholdTextOne.setEnabled(true); this.percentageThresholdCheck.setEnabled(true); this.percentageThresholdCheck.setSelected(true); + this.percentageThresholdTextTwo.setEnabled(true); } private void disablePercentageOptions() { - this.percentageThreshold.setEnabled(false); + this.percentageThresholdTextOne.setEnabled(false); this.percentageThresholdCheck.setEnabled(false); this.percentageThresholdCheck.setSelected(false); + this.percentageThresholdTextTwo.setEnabled(false); } private void handleFileTypeCheckBoxState() { @@ -803,16 +820,18 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private javax.swing.JCheckBox documentsCheckbox; private javax.swing.JLabel errorText; private javax.swing.ButtonGroup fileTypeFilterButtonGroup; + private javax.swing.Box.Filler filler1; + private javax.swing.Box.Filler filler2; private org.sleuthkit.autopsy.commonfilesearch.InterCasePanel interCasePanel; private javax.swing.JRadioButton interCaseRadio; private javax.swing.ButtonGroup interIntraButtonGroup; private org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel intraCasePanel; private javax.swing.JRadioButton intraCaseRadio; - private javax.swing.JLabel jLabel1; private javax.swing.JPanel jPanel1; private java.awt.Panel layoutPanel; - private javax.swing.JTextField percentageThreshold; private javax.swing.JCheckBox percentageThresholdCheck; + private javax.swing.JTextField percentageThresholdTextOne; + private javax.swing.JLabel percentageThresholdTextTwo; private javax.swing.JCheckBox pictureVideoCheckbox; private javax.swing.JButton searchButton; private javax.swing.JRadioButton selectedFileCategoriesButton; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java index b44e79aa68..3aede081f5 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java @@ -93,7 +93,7 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo @Override protected Node createNodeForKey(Integer instanceCount){ - List attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount); + CommonAttributeValueList attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount); return new InstanceCountNode(instanceCount, attributeValues); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java index e8efefa6f4..bac59fde12 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -36,7 +36,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; final public class CommonAttributeSearchResults { // maps instance count to list of attribute values. - private final Map> instanceCountToAttributeValues; + private final Map instanceCountToAttributeValues; private final int percentageThreshold; @@ -46,7 +46,7 @@ final public class CommonAttributeSearchResults { * @param values list of CommonAttributeValue indexed by size of * CommonAttributeValue */ - CommonAttributeSearchResults(Map> metadata, int percentageThreshold) { + CommonAttributeSearchResults(Map metadata, int percentageThreshold) { //wrap in a new object in case any client code has used an unmodifiable collection this.instanceCountToAttributeValues = new HashMap<>(metadata); this.percentageThreshold = percentageThreshold; @@ -61,7 +61,7 @@ final public class CommonAttributeSearchResults { * @param instanceCount key * @return list of values which represent matches */ - List getAttributeValuesForInstanceCount(Integer instanceCount) { + CommonAttributeValueList getAttributeValuesForInstanceCount(Integer instanceCount) { return this.instanceCountToAttributeValues.get(instanceCount); } @@ -72,7 +72,7 @@ final public class CommonAttributeSearchResults { * * @return map of sizes of children to list of matches */ - public Map> getMetadata() throws EamDbException { + public Map getMetadata() throws EamDbException { if(this.percentageThreshold == 0){ return Collections.unmodifiableMap(this.instanceCountToAttributeValues); } else { @@ -86,13 +86,13 @@ final public class CommonAttributeSearchResults { * search. * * Remove results which are not found in the portion of available data - * sources described by minimumPercentageThreshold. + sources described by maximumPercentageThreshold. * * @return metadata */ - private Map> getMetadata(int minimumPercentageThreshold) throws EamDbException { + private Map getMetadata(int maximumPercentageThreshold) throws EamDbException { - if(minimumPercentageThreshold == 0){ + if(maximumPercentageThreshold == 0){ return Collections.unmodifiableMap(this.instanceCountToAttributeValues); } @@ -106,16 +106,16 @@ final public class CommonAttributeSearchResults { Map> itemsToRemove = new HashMap<>(); - for(Entry> listOfValues : Collections.unmodifiableMap(this.instanceCountToAttributeValues).entrySet()){ + for(Entry listOfValues : Collections.unmodifiableMap(this.instanceCountToAttributeValues).entrySet()){ final Integer key = listOfValues.getKey(); - final List values = listOfValues.getValue(); + final CommonAttributeValueList values = listOfValues.getValue(); - for(CommonAttributeValue value : values){ + for(CommonAttributeValue value : values.getDelayedMetadataList()){ // Need the real metadata int frequencyPercentage = eamDb.getFrequencyPercentage(new CorrelationAttributeInstance(fileAttributeType, value.getValue())); - if(frequencyPercentage < minimumPercentageThreshold){ + if(frequencyPercentage > maximumPercentageThreshold){ if(itemsToRemove.containsKey(key)){ itemsToRemove.get(key).add(value); } else { @@ -133,10 +133,10 @@ final public class CommonAttributeSearchResults { final List values = valuesToRemove.getValue(); for (CommonAttributeValue value : values){ - final List instanceCountValue = this.instanceCountToAttributeValues.get(key); - instanceCountValue.remove(value); + final CommonAttributeValueList instanceCountValue = this.instanceCountToAttributeValues.get(key); + instanceCountValue.removeMetaData(value); - if(instanceCountValue.isEmpty()){ + if(instanceCountValue.getDelayedMetadataList().isEmpty()){ // Check the real metadata this.instanceCountToAttributeValues.remove(key); } } @@ -153,8 +153,8 @@ final public class CommonAttributeSearchResults { public int size() { int count = 0; - for (List data : this.instanceCountToAttributeValues.values()) { - for (CommonAttributeValue md5 : data) { + for (CommonAttributeValueList data : this.instanceCountToAttributeValues.values()) { + for(CommonAttributeValue md5 : data.getMetadataList()){ count += md5.getInstanceCount(); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java index 2e9dd4675a..f6372c89f7 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java @@ -39,6 +39,7 @@ final public class CommonAttributeValue { CommonAttributeValue(String md5, List fileInstances) { this.md5 = md5; this.fileInstances = fileInstances; + } CommonAttributeValue(String md5) { @@ -85,4 +86,7 @@ final public class CommonAttributeValue { public int getInstanceCount() { return this.fileInstances.size(); } + + + } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java new file mode 100644 index 0000000000..01690b357b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueList.java @@ -0,0 +1,111 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed 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.sleuthkit.autopsy.commonfilesearch; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Utility and wrapper model around data required for Common Files Search + * results. Subclass this to implement different selections of files from the + * case. + */ +final public class CommonAttributeValueList { + + /** + * The list of value nodes, which begins empty. + */ + private final List metadataList; + + /** + * The backing list of value nodes, which will be dynamically loaded + * when requested. + */ + private final List delayedMetadataList; + + /** + * Create a metadata object containing the list of metadata which can be + * handed off to the node factories. + * + * @param metadata list of Md5Metadata indexed by size of Md5Metadata + */ + CommonAttributeValueList(List metadata) { + this.metadataList = new ArrayList<>(); + this.delayedMetadataList = metadata; + } + + CommonAttributeValueList() { + this.metadataList = new ArrayList<>(); + this.delayedMetadataList = new ArrayList<>(); + } + + /** + * Get the list of value nodes. Will be empty if + * displayDelayedMetadata() has not been called for the + * parent InstanceCountNode + * @return metadataList the list of nodes + */ + public List getMetadataList() { + return Collections.unmodifiableList(this.metadataList); + } + + /** + * Get the delayed list of value nodes. Only use for + * determining how many CommonAttributeValues + * actually exist in the list. + * @return metadataList the list of nodes + */ + List getDelayedMetadataList() { + return Collections.unmodifiableList(this.delayedMetadataList); + } + + void removeMetaData(CommonAttributeValue commonVal) { + this.delayedMetadataList.remove(commonVal); + } + + /** + * Return the size of the backing list, in case + * displayDelayedMetadata() has not be called yet. + * @return int the number of matches for this value + */ + int getCommonAttributeListSize() { + return this.delayedMetadataList.size(); + } + + /** + * Dynamically load the list CommonAttributeValue when called. Until called + * metadataList should be empty. The parent node, InstanceCountNode, will + * trigger the factory call and refresh. + */ + public void displayDelayedMetadata() { + if (metadataList.isEmpty()) { + this.metadataList.addAll(this.delayedMetadataList); + } + } + + /** + * A a value node to the list, to be loaded later. + * @param metadata the node to add + */ + void addMetadataToList(CommonAttributeValue metadata) { + delayedMetadataList.add(metadata); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java index c7478f3fc4..97a41b27be 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java @@ -29,6 +29,7 @@ import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; +import org.sleuthkit.autopsy.corecomponents.DelayedLoadChildNodesOnTreeExpansion; /** * DataResultViewerTable which overrides the default column @@ -59,7 +60,19 @@ public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTa COLUMN_WIDTHS = Collections.unmodifiableMap(map); } - + /** + * Implements a DataResultViewerTable which constructs a tabular result viewer that + * displays the children of the given root node using an OutlineView. The explorer + * manager will be discovered at runtime. + * + * Adds a TreeExpansionsListener to the outlineView to receive tree expansion events + * which dynamically loads children nodes when requested. + */ + public CommonAttributesSearchResultsViewerTable() { + super(); + outlineView.addTreeExpansionListener(new DelayedLoadChildNodesOnTreeExpansion()); + } + @NbBundle.Messages({ "CommonFilesSearchResultsViewerTable.noDescText= ", "CommonFilesSearchResultsViewerTable.filesColLbl=Files", diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java index dbc3d362e9..fa298c121d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java @@ -19,7 +19,6 @@ */ package org.sleuthkit.autopsy.commonfilesearch; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -29,52 +28,68 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.NodeProperty; /** - * Node used to indicate the number of matches found with the MD5 children - * of this Node. + * Node used to indicate the number of matches found with the MD5 children of + * this Node. */ final public class InstanceCountNode extends DisplayableItemNode { + private static final Logger logger = Logger.getLogger(InstanceCountNode.class.getName()); + final private int instanceCount; - final private List attributeValues; + final private CommonAttributeValueList attributeValues; /** - * Create a node with the given number of instances, and the given - * selection of metadata. + * Create a node with the given number of instances, and the given selection + * of metadata. + * * @param instanceCount - * @param attributeValues + * @param attributeValues */ @NbBundle.Messages({ "InstanceCountNode.displayName=Files with %s instances (%s)" }) - public InstanceCountNode(int instanceCount, List attributeValues) { - super(Children.create(new CommonAttributeValueNodeFactory(attributeValues), true)); + public InstanceCountNode(int instanceCount, CommonAttributeValueList attributeValues) { + super(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), true)); this.instanceCount = instanceCount; this.attributeValues = attributeValues; - - this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), attributeValues.size())); + + this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), attributeValues.getCommonAttributeListSize())); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS } /** * Number of matches found for each of the MD5 children. + * * @return int match count */ int getInstanceCount() { return this.instanceCount; } + /** + * Refresh the node, by dynamically loading in the children when called, and + * calling the CommonAttributeValueNodeFactory to generate nodes for the + * children in attributeValues. + */ + public void refresh() { + attributeValues.displayDelayedMetadata(); + setChildren(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), true)); + } + /** * Get a list of metadata for the MD5s which are children of this object. + * * @return List */ - List getAttributeValues() { - return Collections.unmodifiableList(this.attributeValues); + CommonAttributeValueList getAttributeValues() { + return this.attributeValues; } @Override @@ -101,17 +116,23 @@ final public class InstanceCountNode extends DisplayableItemNode { sheetSet = Sheet.createPropertiesSet(); sheet.put(sheetSet); } - + final String NO_DESCR = Bundle.InstanceCountNode_createSheet_noDescription(); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, "")); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), NO_DESCR, this.getInstanceCount())); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), NO_DESCR, "")); + return sheet; } - /** * ChildFactory which builds CommonFileParentNodes from the - * CommonFilesMetaaData models. + * CommonAttributeValue metadata models. */ static class CommonAttributeValueNodeFactory extends ChildFactory { @@ -138,11 +159,11 @@ final public class InstanceCountNode extends DisplayableItemNode { list.addAll(this.metadata.keySet()); return true; } - + @Override protected Node createNodeForKey(String attributeValue) { CommonAttributeValue md5Metadata = this.metadata.get(attributeValue); return new CommonAttributeValueNode(md5Metadata); } } -} \ No newline at end of file +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java index 9f772d9958..5a1339e487 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java @@ -20,10 +20,8 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; @@ -140,7 +138,7 @@ final class InterCaseSearchResultsProcessor { * * @param currentCase The current TSK Case. */ - Map> findInterCaseCommonAttributeValues(Case currentCase) { + Map findInterCaseCommonAttributeValues(Case currentCase) { try { InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback(); EamDb DbManager = EamDb.getInstance(); @@ -167,7 +165,7 @@ final class InterCaseSearchResultsProcessor { * @param currentCase The current TSK Case. * @param singleCase The case of interest. Matches must exist in this case. */ - Map> findSingleInterCaseCommonAttributeValues(Case currentCase, CorrelationCase singleCase) { + Map findSingleInterCaseCommonAttributeValues(Case currentCase, CorrelationCase singleCase) { try { InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback(); EamDb DbManager = EamDb.getInstance(); @@ -188,7 +186,7 @@ final class InterCaseSearchResultsProcessor { */ private class InterCaseCommonAttributesCallback implements InstanceTableCallback { - final Map> instanceCollatedCommonFiles = new HashMap<>(); + final Map instanceCollatedCommonFiles = new HashMap<>(); private CommonAttributeValue commonAttributeValue = null; private String previousRowMd5 = ""; @@ -234,10 +232,10 @@ final class InterCaseSearchResultsProcessor { if (!corValue.equals(previousRowMd5)) { int size = commonAttributeValue.getInstanceCount(); if (instanceCollatedCommonFiles.containsKey(size)) { - instanceCollatedCommonFiles.get(size).add(commonAttributeValue); + instanceCollatedCommonFiles.get(size).addMetadataToList(commonAttributeValue); } else { - ArrayList value = new ArrayList<>(); - value.add(commonAttributeValue); + CommonAttributeValueList value = new CommonAttributeValueList(); + value.addMetadataToList(commonAttributeValue); instanceCollatedCommonFiles.put(size, value); } @@ -251,7 +249,7 @@ final class InterCaseSearchResultsProcessor { commonAttributeValue.addInstance(searchResult); } - Map> getInstanceCollatedCommonFiles() { + Map getInstanceCollatedCommonFiles() { return Collections.unmodifiableMap(instanceCollatedCommonFiles); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java index 09fc02714a..569ae5232f 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java @@ -128,7 +128,7 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt } } - Map> instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); + Map instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); return new CommonAttributeSearchResults(instanceCollatedCommonFiles, this.frequencyPercentageThreshold); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java index fcc68b565c..d07c31cfeb 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java @@ -75,7 +75,7 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri CommonAttributeSearchResults findFiles(CorrelationCase correlationCase) throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap(), corAttrType); - Map> interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase); + Map interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase); return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java index 5b3e806bd4..3e09e32193 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import org.openide.util.NbBundle; /** * Manager for present state of errors on the Common Files Search. @@ -39,14 +40,17 @@ class UserInputErrorManager { * of all known error states, retrieve error messages, and determine if * anything is in an error state. */ + @NbBundle.Messages({ + "UserInputErrorManager.frequency=Invalid Frequency Percentage: 0 < % < 100.", + "UserInputErrorManager.categories=No file categories are included in the search."}) UserInputErrorManager (){ //when new errors are needed for the dialog, define a key and a value // and add them to the map. this.currentErrors = new HashMap<>(); - this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage("Frequency percentage must be greater than zero and less than or equal to 100.")); - this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage("No file categories are included in the search.")); + this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage(Bundle.UserInputErrorManager_frequency())); + this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage(Bundle.UserInputErrorManager_categories())); } /** diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form index d6c32623a4..8ad47b32c7 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form @@ -29,6 +29,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 80d42614c1..024686e26f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -102,6 +102,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { public DataResultViewerTable() { this(null, Bundle.DataResultViewerTable_title()); } + /** * Constructs a tabular result viewer that displays the children of a given @@ -179,13 +180,14 @@ public class DataResultViewerTable extends AbstractDataResultViewer { /** * Gets the title of this tabular result viewer. - * @return title of tab. + * @return */ @Override @NbBundle.Messages("DataResultViewerTable.title=Table") public String getTitle() { return title; } + /** * Indicates whether a given node is supported as a root node for this @@ -837,7 +839,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private org.openide.explorer.view.OutlineView outlineView; + protected org.openide.explorer.view.OutlineView outlineView; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java new file mode 100644 index 0000000000..6e6d28af2a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DelayedLoadChildNodesOnTreeExpansion.java @@ -0,0 +1,54 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed 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.sleuthkit.autopsy.corecomponents; + +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import org.openide.explorer.view.Visualizer; +import org.openide.nodes.Node; + +/** + * A tree expansion listener that will trigger a recreation of childs through + * its child factory on re-expansion of a node (causes to recreate the + * ChildFactory for this purpose.). + */ +public final class DelayedLoadChildNodesOnTreeExpansion implements TreeExpansionListener { + + /** + * A flag for avoiding endless recursion inside the expansion listener that + * could trigger collapsing and (re-)expanding nodes again. + * @param event + */ + + @Override + public synchronized void treeCollapsed(final TreeExpansionEvent event) { + // Do nothing on collapse. Netbeans should manage nodes falling out of scope and GC. + } + + @Override + public synchronized void treeExpanded(final TreeExpansionEvent event) { + Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent()); + if (eventNode instanceof TableFilterNode) { + final TableFilterNode node = (TableFilterNode) eventNode; + node.refresh(); + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java b/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java index eb36cf2e87..2ba02f694a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/TableFilterNode.java @@ -128,6 +128,16 @@ public class TableFilterNode extends FilterNode { return null; } } + + /** + * Refreshes the inner node, which depending on the actual node type that was wrapped + * could trigger a dynamic refresh of the children, if supported. + */ + void refresh() { + DataResultFilterNode innerNode = getLookup().lookup(DataResultFilterNode.class); + innerNode.refresh(); + + } /** * @return the column order key, which allows custom column ordering to be diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 098789597c..c94ad57094 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -132,26 +132,38 @@ public class DataResultFilterNode extends FilterNode { * wrapped node and may filter out some of its children. * * @param node The node to wrap. - * @param em The ExplorerManager for the component that is creating the - * node. + * @param em The ExplorerManager for the component that is creating the + * node. */ public DataResultFilterNode(Node node, ExplorerManager em) { super(node, new DataResultFilterChildren(node, em)); this.sourceEm = em; } + /** + * Refreshes the inner node. If the actual underlying node is an InstanceCountNode, + * refresh() that node, which refreshes the children. + * + */ + public void refresh() { + if (getOriginal() instanceof InstanceCountNode) { + InstanceCountNode innerNode = getLookup().lookup(InstanceCountNode.class); + innerNode.refresh(); + } + } + /** * Constructs a node used to wrap another node before passing it to the * result viewers. The wrapper node defines the actions associated with the * wrapped node and may filter out some of its children. * - * @param node The node to wrap. - * @param em The ExplorerManager for the component that is creating - * the node. + * @param node The node to wrap. + * @param em The ExplorerManager for the component that is creating the + * node. * @param filterKnown Whether or not to filter out children that represent - * known files. + * known files. * @param filterSlack Whether or not to filter out children that represent - * virtual slack space files. + * virtual slack space files. */ private DataResultFilterNode(Node node, ExplorerManager em, boolean filterKnown, boolean filterSlack) { super(node, new DataResultFilterChildren(node, em, filterKnown, filterSlack)); @@ -261,7 +273,7 @@ public class DataResultFilterNode extends FilterNode { * selected. * * @return The child node selection information, or null if no child should - * be selected. + * be selected. */ public NodeSelectionInfo getChildNodeSelectionInfo() { if (getOriginal() instanceof DisplayableItemNode) { @@ -390,14 +402,14 @@ public class DataResultFilterNode extends FilterNode { NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewFileInDir.text"), c)); } // action to go to the source file of the artifact - // action to go to the source file of the artifact + // action to go to the source file of the artifact Content fileContent = ban.getLookup().lookup(AbstractFile.class); if (fileContent == null) { Content content = ban.getLookup().lookup(Content.class); actionsList.add(new ViewContextAction("View Source Content in Directory", content)); } else { actionsList.add(new ViewContextAction( - NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewSrcFileInDir.text"), ban)); + NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewSrcFileInDir.text"), ban)); } } Content c = ban.getLookup().lookup(File.class); @@ -524,21 +536,21 @@ public class DataResultFilterNode extends FilterNode { */ private class GetPreferredActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default { - @Override - public AbstractAction visit(InstanceCountNode icn){ + @Override + public AbstractAction visit(InstanceCountNode icn) { return null; } - + @Override public AbstractAction visit(CommonAttributeValueNode md5n){ return null; } - + @Override public AbstractAction visit(CaseDBCommonAttributeInstanceNode fin){ return null; } - + @Override public AbstractAction visit(CentralRepoCommonAttributeInstanceNode iccan){ return null; diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java index b4f6302d61..2791f725a7 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java @@ -57,64 +57,36 @@ import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstance import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValue; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueList; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.datamodel.AbstractFile; /** * Utilities for testing intercase correlation feature. - * - * This will be more useful when we add more flush out the intercase - * correlation features and need to add more tests. In particular, - * testing scenarios where we need different cases to be the current case - * will suggest that we create additional test classes, and we will want to - * import this utility in each new intercase test file. - * - * Description of Test Data: - (Note: files of the same name and extension are identical; - files of the same name and differing extension are not identical.) - - Case 1 - +Data Set 1 - - Hash-0.dat [testFile of size 0] - - Hash-A.jpg - - Hash-A.pdf - +Data Set2 - - Hash-0.dat [testFile of size 0] - - Hash-A.jpg - - Hash-A.pdf - Case 2 - +Data Set 1 - - Hash-B.jpg - - Hash-B.pdf - +Data Set 2 - - Hash-A.jpg - - Hash-A.pdf - - Hash_D.doc - Case 3 - +Data Set 1 - - Hash-A.jpg - - Hash-A.pdf - - Hash-C.jpg - - Hash-C.pdf - - Hash-D.jpg - +Data Set 2 - - Hash-C.jpg - - Hash-C.pdf - - Hash-D.doc - * - * Frequency Breakdown - * (ratio of datasources a given file appears in to total number of datasources): - * - * Hash-0.dat - moot; these are always excluded - * Hash-A.jpg - 4/6 - * Hash-A.pdf - 4/6 - * Hash-B.jpg - 1/6 - * Hash-B.pdf - 1/6 - * Hash-C.jpg - 2/6 - * Hash-C.pdf - 2/6 - * Hash_D.doc - 2/6 - * Hash-D.jpg - 1/6 - * + * + * This will be more useful when we add more flush out the intercase correlation + * features and need to add more tests. In particular, testing scenarios where + * we need different cases to be the current case will suggest that we create + * additional test classes, and we will want to import this utility in each new + * intercase test file. + * + * Description of Test Data: (Note: files of the same name and extension are + * identical; files of the same name and differing extension are not identical.) + * + * Case 1 +Data Set 1 - Hash-0.dat [testFile of size 0] - Hash-A.jpg - + * Hash-A.pdf +Data Set2 - Hash-0.dat [testFile of size 0] - Hash-A.jpg - + * Hash-A.pdf Case 2 +Data Set 1 - Hash-B.jpg - Hash-B.pdf +Data Set 2 - + * Hash-A.jpg - Hash-A.pdf - Hash_D.doc Case 3 +Data Set 1 - Hash-A.jpg - + * Hash-A.pdf - Hash-C.jpg - Hash-C.pdf - Hash-D.jpg +Data Set 2 - Hash-C.jpg - + * Hash-C.pdf - Hash-D.doc + * + * Frequency Breakdown (ratio of datasources a given file appears in to total + * number of datasources): + * + * Hash-0.dat - moot; these are always excluded Hash-A.jpg - 4/6 Hash-A.pdf - + * 4/6 Hash-B.jpg - 1/6 Hash-B.pdf - 1/6 Hash-C.jpg - 2/6 Hash-C.pdf - 2/6 + * Hash_D.doc - 2/6 Hash-D.jpg - 1/6 + * */ class InterCaseTestUtils { @@ -187,21 +159,21 @@ class InterCaseTestUtils { this.dataSourceLoader = new DataSourceLoader(); } - void clearTestDir(){ - if(CASE_DIRECTORY_PATH.toFile().exists()){ - try{ - if(EamDb.isEnabled()) { + void clearTestDir() { + if (CASE_DIRECTORY_PATH.toFile().exists()) { + try { + if (EamDb.isEnabled()) { EamDb.getInstance().shutdownConnections(); } FileUtils.deleteDirectory(CASE_DIRECTORY_PATH.toFile()); - } catch(IOException | EamDbException ex){ + } catch (IOException | EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); } } CASE_DIRECTORY_PATH.toFile().exists(); } - + Map getDataSourceMap() throws NoCurrentCaseException, TskCoreException, SQLException { return this.dataSourceLoader.getDataSourceMap(); } @@ -238,7 +210,7 @@ class InterCaseTestUtils { if (!crSettings.dbDirectoryExists()) { crSettings.createDbDirectory(); } - + crSettings.initializeDatabaseSchema(); crSettings.insertDefaultDatabaseContent(); @@ -317,59 +289,59 @@ class InterCaseTestUtils { return null; } } - - static boolean verifyInstanceExistanceAndCount(CommonAttributeSearchResults searchDomain, String fileName, String dataSource, String crCase, int instanceCount){ - + + static boolean verifyInstanceExistanceAndCount(CommonAttributeSearchResults searchDomain, String fileName, String dataSource, String crCase, int instanceCount) { + try { int tally = 0; - - for(Map.Entry> entry : searchDomain.getMetadata().entrySet()){ - - for(CommonAttributeValue value : entry.getValue()){ - - for(AbstractCommonAttributeInstance commonAttribute : value.getInstances()){ - - if(commonAttribute instanceof CentralRepoCommonAttributeInstance){ + + for (Map.Entry entry : searchDomain.getMetadata().entrySet()) { + entry.getValue().displayDelayedMetadata(); + for (CommonAttributeValue value : entry.getValue().getMetadataList()) { + + for (AbstractCommonAttributeInstance commonAttribute : value.getInstances()) { + + if (commonAttribute instanceof CentralRepoCommonAttributeInstance) { CentralRepoCommonAttributeInstance results = (CentralRepoCommonAttributeInstance) commonAttribute; - for (DisplayableItemNode din : results.generateNodes()){ - - if(din instanceof CentralRepoCommonAttributeInstanceNode){ - + for (DisplayableItemNode din : results.generateNodes()) { + + if (din instanceof CentralRepoCommonAttributeInstanceNode) { + CentralRepoCommonAttributeInstanceNode node = (CentralRepoCommonAttributeInstanceNode) din; CorrelationAttributeInstance instance = node.getCorrelationAttributeInstance(); - + final String fullPath = instance.getFilePath(); final File testFile = new File(fullPath); - + final String testCaseName = instance.getCorrelationCase().getDisplayName(); - + final String testFileName = testFile.getName(); - + final String testDataSource = instance.getCorrelationDataSource().getName(); - + boolean sameFileName = testFileName.equalsIgnoreCase(fileName); boolean sameDataSource = testDataSource.equalsIgnoreCase(dataSource); boolean sameCrCase = testCaseName.equalsIgnoreCase(crCase); - - if( sameFileName && sameDataSource && sameCrCase){ + + if (sameFileName && sameDataSource && sameCrCase) { tally++; } } - - if(din instanceof CaseDBCommonAttributeInstanceNode){ - + + if (din instanceof CaseDBCommonAttributeInstanceNode) { + CaseDBCommonAttributeInstanceNode node = (CaseDBCommonAttributeInstanceNode) din; AbstractFile file = node.getContent(); - + final String testFileName = file.getName(); final String testCaseName = node.getCase(); final String testDataSource = node.getDataSource(); - + boolean sameFileName = testFileName.equalsIgnoreCase(fileName); boolean sameCaseName = testCaseName.equalsIgnoreCase(crCase); boolean sameDataSource = testDataSource.equalsIgnoreCase(dataSource); - - if(sameFileName && sameDataSource && sameCaseName){ + + if (sameFileName && sameDataSource && sameCaseName) { tally++; } } @@ -378,10 +350,11 @@ class InterCaseTestUtils { Assert.fail("Unable to cast AbstractCommonAttributeInstanceNode to InterCaseCommonAttributeSearchResults."); } } - } + } } - + return tally == instanceCount; + } catch (EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex.getMessage()); @@ -394,13 +367,13 @@ class InterCaseTestUtils { * central repo db. */ void tearDown() { - + CaseUtils.closeCurrentCase(false); - - String[] cases = new String[]{CASE1,CASE2,CASE3}; - + + String[] cases = new String[]{CASE1, CASE2, CASE3}; + try { - for(String caze : cases){ + for (String caze : cases) { CaseUtils.deleteCaseDir(new File(caze)); } } catch (IOException ex) { diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java index 6067b78906..7cbae0af36 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java @@ -38,6 +38,7 @@ import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeInstance; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValue; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueList; import org.sleuthkit.autopsy.testutils.CaseUtils; import org.sleuthkit.autopsy.testutils.IngestUtils; import org.sleuthkit.datamodel.AbstractFile; @@ -50,24 +51,14 @@ import org.sleuthkit.datamodel.TskCoreException; * * Data set definitions: * - * set 1 - * + file1 - * - IMG_6175.jpg - * + file2 - * - IMG_6175.jpg - * + file3 - * - BasicStyleGuide.doc + * set 1 + file1 - IMG_6175.jpg + file2 - IMG_6175.jpg + file3 - + * BasicStyleGuide.doc * - * set 2 - * - adsf.pdf - * - IMG_6175.jpg + * set 2 - adsf.pdf - IMG_6175.jpg * - * set 3 - * - BasicStyleGuide.doc - * - IMG_6175.jpg + * set 3 - BasicStyleGuide.doc - IMG_6175.jpg * - * set 4 - * - file.dat (empty file) + * set 4 - file.dat (empty file) */ class IntraCaseTestUtils { @@ -92,13 +83,13 @@ class IntraCaseTestUtils { private final DataSourceLoader dataSourceLoader; private final String caseName; - - IntraCaseTestUtils(NbTestCase nbTestCase, String caseName){ + + IntraCaseTestUtils(NbTestCase nbTestCase, String caseName) { this.imagePath1 = Paths.get(nbTestCase.getDataDir().toString(), SET1); this.imagePath2 = Paths.get(nbTestCase.getDataDir().toString(), SET2); this.imagePath3 = Paths.get(nbTestCase.getDataDir().toString(), SET3); this.imagePath4 = Paths.get(nbTestCase.getDataDir().toString(), SET4); - + this.dataSourceLoader = new DataSourceLoader(); this.caseName = caseName; @@ -197,24 +188,25 @@ class IntraCaseTestUtils { } /** - * Create a convenience lookup table mapping file instance object ids to - * the data source they appear in. - * - * @param metadata object returned by the code under test + * Create a convenience lookup table mapping file instance object ids to the + * data source they appear in. + * + * @param metadata object returned by the code under test * @return mapping of objectId to data source name */ static Map mapFileInstancesToDataSources(CommonAttributeSearchResults metadata) { Map instanceIdToDataSource = new HashMap<>(); - + try { - for (Map.Entry> entry : metadata.getMetadata().entrySet()) { - for (CommonAttributeValue md : entry.getValue()) { + for (Map.Entry entry : metadata.getMetadata().entrySet()) { + entry.getValue().displayDelayedMetadata(); + for (CommonAttributeValue md : entry.getValue().getMetadataList()) { for (AbstractCommonAttributeInstance fim : md.getInstances()) { instanceIdToDataSource.put(fim.getAbstractFileObjectId(), fim.getDataSource()); } } } - + return instanceIdToDataSource; } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -223,7 +215,6 @@ class IntraCaseTestUtils { } } - static List getFiles(Set objectIds) { List files = new ArrayList<>(objectIds.size()); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index 8dddd2a3e6..e8500e6ba2 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -1398,10 +1398,6 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen AutoIngestJob job; if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) { job = new AutoIngestJob(nodeData); - Path caseDirectory = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); - if (null != caseDirectory) { - job.setCaseDirectoryPath(caseDirectory); - } } else { job = new AutoIngestJob(manifest); job.setPriority(nodeData.getPriority()); // Retain priority, present in all versions of the node data. @@ -1431,10 +1427,6 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen sysLogger.log(Level.SEVERE, String.format("Error attempting to set node data for %s", manifest.getFilePath()), ex); } } - Path caseDirectory = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); - if (null != caseDirectory) { - job.setCaseDirectoryPath(caseDirectory); - } newPendingJobsList.add(job); } @@ -1595,54 +1587,53 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * @throws InterruptedException */ private void addCompletedJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws CoordinationServiceException, InterruptedException, AutoIngestJobException { - Path caseDirectoryPath = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); - if (null != caseDirectoryPath) { - AutoIngestJob job; - if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) { - job = new AutoIngestJob(nodeData); - job.setCaseDirectoryPath(caseDirectoryPath); - } else { - /** - * Use the manifest rather than the node data here to create - * a new AutoIngestJob instance because the AutoIngestJob - * constructor that takes a node data object expects the - * node data to have fields that do not exist in earlier - * versions. - */ - job = new AutoIngestJob(manifest); - job.setCaseDirectoryPath(caseDirectoryPath); + Path caseDirectoryPath = nodeData.getCaseDirectoryPath(); + if (!caseDirectoryPath.toFile().exists()) { + sysLogger.log(Level.WARNING, String.format("Job completed for %s, but cannot find case directory %s, ignoring job", nodeData.getManifestFilePath(), caseDirectoryPath.toString())); + return; + } - /** - * Update the job with the fields that exist in all versions - * of the nodeData. - */ - job.setCompletedDate(nodeData.getCompletedDate()); - job.setErrorsOccurred(nodeData.getErrorsOccurred()); - job.setPriority(nodeData.getPriority()); - job.setNumberOfCrashes(nodeData.getNumberOfCrashes()); - job.setProcessingStage(AutoIngestJob.Stage.COMPLETED, nodeData.getCompletedDate()); - job.setProcessingStatus(AutoIngestJob.ProcessingStatus.COMPLETED); + AutoIngestJob job; + if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) { + job = new AutoIngestJob(nodeData); + job.setCaseDirectoryPath(caseDirectoryPath); + } else { + /** + * Use the manifest rather than the node data here to create a + * new AutoIngestJob instance because the AutoIngestJob + * constructor that takes a node data object expects the node + * data to have fields that do not exist in earlier versions. + */ + job = new AutoIngestJob(manifest); + job.setCaseDirectoryPath(caseDirectoryPath); - /* + /** + * Update the job with the fields that exist in all versions of + * the nodeData. + */ + job.setCompletedDate(nodeData.getCompletedDate()); + job.setErrorsOccurred(nodeData.getErrorsOccurred()); + job.setPriority(nodeData.getPriority()); + job.setNumberOfCrashes(nodeData.getNumberOfCrashes()); + job.setProcessingStage(AutoIngestJob.Stage.COMPLETED, nodeData.getCompletedDate()); + job.setProcessingStatus(AutoIngestJob.ProcessingStatus.COMPLETED); + + /* * Try to upgrade/update the coordination service manifest * node data for the job. It is possible that two hosts will * both try to obtain the lock to do the upgrade operation * at the same time. If this happens, the host that is * holding the lock will complete the upgrade operation. - */ - try (Lock manifestLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.MANIFESTS, manifest.getFilePath().toString())) { - if (null != manifestLock) { - updateCoordinationServiceManifestNode(job); - } - } catch (CoordinationServiceException ex) { - sysLogger.log(Level.SEVERE, String.format("Error attempting to set node data for %s", manifest.getFilePath()), ex); + */ + try (Lock manifestLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.MANIFESTS, manifest.getFilePath().toString())) { + if (null != manifestLock) { + updateCoordinationServiceManifestNode(job); } + } catch (CoordinationServiceException ex) { + sysLogger.log(Level.SEVERE, String.format("Error attempting to set node data for %s", manifest.getFilePath()), ex); } - newCompletedJobsList.add(job); - - } else { - sysLogger.log(Level.WARNING, String.format("Job completed for %s, but cannot find case directory, ignoring job", nodeData.getManifestFilePath())); } + newCompletedJobsList.add(job); } /** @@ -2456,6 +2447,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen Thread.sleep(AutoIngestUserPreferences.getSecondsToSleepBetweenCases() * 1000); } currentJob.setCaseDirectoryPath(caseDirectoryPath); + updateCoordinationServiceManifestNode(currentJob); // update case directory path Case caseForJob = Case.getCurrentCase(); sysLogger.log(Level.INFO, "Opened case {0} for {1}", new Object[]{caseForJob.getName(), manifest.getFilePath()}); return caseForJob;