diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeDataCollector.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeDataCollector.java new file mode 100755 index 0000000000..0f9554aeee --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobNodeDataCollector.java @@ -0,0 +1,58 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019-2019 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.experimental.autoingest; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * Collects the auto ingest job node data stored in the manifest file + * coordination service nodes. + */ +final class AutoIngestJobNodeDataCollector { + + private static final Logger logger = Logger.getLogger(AutoIngestJobNodeDataCollector.class.getName()); + + static List getNodeData() throws CoordinationServiceException, InterruptedException { + final CoordinationService coordinationService = CoordinationService.getInstance(); + final List nodePaths = coordinationService.getNodeList(CoordinationService.CategoryNode.MANIFESTS); + final List nodeDataList = new ArrayList<>(); + for (String nodePath : nodePaths) { + try { + final byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, nodePath); + AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(nodeBytes); + nodeDataList.add(nodeData); + } catch (AutoIngestJobNodeData.InvalidDataException ex) { + logger.log(Level.WARNING, String.format("Error reading node data from manifest file coordination service node %s", nodePath), ex); // NON-NLS + } + } + return nodeDataList; + } + + /** + * Prevents instantiation of this utility class. + */ + private AutoIngestJobNodeDataCollector() { + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseNodesCleanupAction.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/BackgroundTaskAction.java similarity index 51% rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseNodesCleanupAction.java rename to Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/BackgroundTaskAction.java index a54308047d..1140b49cf1 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseNodesCleanupAction.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/BackgroundTaskAction.java @@ -19,39 +19,60 @@ package org.sleuthkit.autopsy.experimental.autoingest; import java.awt.event.ActionEvent; -import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import javax.swing.AbstractAction; -import org.openide.util.Exceptions; -import org.openide.util.NbBundle; import org.sleuthkit.autopsy.progress.AppFrameProgressBar; +import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.autopsy.progress.TaskCancellable; /** - * An action class that kicks off a cancellable case nodes cleanup task that - * runs in a background thread and reports progress using an application frame - * progress bar. + * A base class for action classes that kick off a cancellable task that runs in + * a background thread and reports progress using an application frame progress + * bar. */ -final class CaseNodesCleanupAction extends AbstractAction { +abstract class BackgroundTaskAction extends AbstractAction { private static final long serialVersionUID = 1L; + private final String progressDisplayName; + + /** + * Constructs the base class part of action classes that kick off a + * cancellable task that runs in a background thread and reports progress + * using an application frame progress bar. + * + * @param actionName The name of the action. + * @param progressDisplayName The display name for the progress bar. + */ + BackgroundTaskAction(String actionName, String progressDisplayName) { + super(actionName); + this.progressDisplayName = progressDisplayName; + } @Override - @NbBundle.Messages({ - "CaseNodesCleanupAction.progressDisplayName=Cleanup Case Znodes" - }) public void actionPerformed(ActionEvent event) { - final AppFrameProgressBar progress = new AppFrameProgressBar(Bundle.CaseNodesCleanupAction_progressDisplayName()); + final AppFrameProgressBar progress = new AppFrameProgressBar(progressDisplayName); final TaskCancellable taskCanceller = new TaskCancellable(progress); progress.setCancellationBehavior(taskCanceller); - final Runnable task = new CaseNodesCleanupTask(progress); + final Runnable task = getTask(progress); final FutureTask future = new FutureTask<>(task, null); taskCanceller.setFuture(future); new Thread(future).start(); } + /** + * Gets the background task to be executed. The task is expected to report + * its progress using the supplied progress indicator and to check for + * cancellation by checking to see if the thread it is running in has been + * interrupted. + * + * @param progress A progress indicator for the task. + * + * @return The Runnnable task. + */ + abstract Runnable getTask(ProgressIndicator progress); + @Override - public CaseNodesCleanupAction clone() throws CloneNotSupportedException { + public BackgroundTaskAction clone() throws CloneNotSupportedException { super.clone(); throw new CloneNotSupportedException(); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties index e756c0b2ad..f3081bef89 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties @@ -255,4 +255,5 @@ AinStatusDashboard.nodeStatusTableTitle.text=Auto Ingest Nodes AinStatusDashboard.healthMonitorButton.text=Health Monitor CasesDashboardTopComponent.refreshButton.text=Refresh AutoIngestCasesDeletionDialog.jLabel1.text=Progress -CasesDashboardTopComponent.cleanCaseNodesButton.text=Clean Case Znodes +CasesDashboardTopComponent.deleteOrphanCaseNodesButton.text=Delete Orphan Case Znodes +CasesDashboardTopComponent.deleteOrphanManifestNodesButton.text=Delete Orphan Manifest Znodes diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED index 1753d15d1e..38d8ee47a1 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED @@ -138,12 +138,6 @@ AutoIngestJobsNode.status.text=Status AutoIngestJobsPanel.waitNode.text=Please Wait... AutoIngestMetricsDialog.initReportText=Select a date above and click the 'Generate Metrics Report' button to generate\na metrics report. AutoIngestMetricsDialog.title.text=Auto Ingest Metrics -CaseNodesCleanupAction.progressDisplayName=Cleanup Case Znodes -CaseNodesCleanupTask.progress.connectingToCoordSvc=Connecting to the coordination service... -# {0} - node path -CaseNodesCleanupTask.progress.deletingOrphanedCaseNode=Deleting orphaned case node {0}... -CaseNodesCleanupTask.progress.gettingCaseNodesListing=Querying coordination service for case nodes... -CaseNodesCleanupTask.progress.startMessage=Starting orphaned case znode cleanup... ConfirmationDialog.DoNotDelete=Do not delete ConfirmationDialog.Delete=Permanently delete ConfirmationDialog.DeleteAreYouSure=The entire case will be removed. Are you sure you want to delete case @@ -210,6 +204,18 @@ DeleteCaseTask.progress.parsingManifest=Parsing manifest file {0}... # {0} - manifest file path DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}... DeleteCaseTask.progress.startMessage=Starting deletion... +DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes +DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service +# {0} - node path +DeleteOrphanCaseNodesTask.progress.deletingOrphanedCaseNode=Deleting orphaned case znode {0} +DeleteOrphanCaseNodesTask.progress.gettingCaseNodesListing=Querying coordination service for case znodes +DeleteOrphanCaseNodesTask.progress.startMessage=Starting orphaned case znode cleanup +DeleteOrphanManifestNodesAction.progressDisplayName=Cleanup Manifest File Znodes +DeleteOrphanManifestNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service +# {0} - node path +DeleteOrphanManifestNodesTask.progress.deletingOrphanedManifestNode=Deleting orphaned manifest file znode {0} +DeleteOrphanManifestNodesTask.progress.gettingManifestNodes=Querying the coordination service for manifest file znodes +DeleteOrphanManifestNodesTask.progress.startMessage=Starting orphaned manifest file znode cleanup HINT_CasesDashboardTopComponent=This is an adminstrative dashboard for multi-user cases OpenAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted. OpenAutoIngestLogAction.logOpenFailedErrorMsg=Failed to open case auto ingest log. See application log for details. @@ -446,4 +452,5 @@ AinStatusDashboard.nodeStatusTableTitle.text=Auto Ingest Nodes AinStatusDashboard.healthMonitorButton.text=Health Monitor CasesDashboardTopComponent.refreshButton.text=Refresh AutoIngestCasesDeletionDialog.jLabel1.text=Progress -CasesDashboardTopComponent.cleanCaseNodesButton.text=Clean Case Znodes +CasesDashboardTopComponent.deleteOrphanCaseNodesButton.text=Delete Orphan Case Znodes +CasesDashboardTopComponent.deleteOrphanManifestNodesButton.text=Delete Orphan Manifest Znodes diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CasesDashboardTopComponent.form b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CasesDashboardTopComponent.form index dd74bacc5c..3ade741f25 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CasesDashboardTopComponent.form +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CasesDashboardTopComponent.form @@ -20,10 +20,12 @@ - + - - + + + + @@ -40,8 +42,9 @@ - - + + + @@ -63,14 +66,24 @@ - + - + - + + + + + + + + + + + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CasesDashboardTopComponent.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CasesDashboardTopComponent.java index 5986593f88..dcd7fa1fab 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CasesDashboardTopComponent.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CasesDashboardTopComponent.java @@ -119,7 +119,8 @@ public final class CasesDashboardTopComponent extends TopComponent implements Ex refreshButton = new javax.swing.JButton(); caseBrowserScrollPane = new javax.swing.JScrollPane(); - cleanCaseNodesButton = new javax.swing.JButton(); + deleteOrphanCaseNodesButton = new javax.swing.JButton(); + deleteOrphanManifestNodesButton = new javax.swing.JButton(); org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(CasesDashboardTopComponent.class, "CasesDashboardTopComponent.refreshButton.text")); // NOI18N refreshButton.addActionListener(new java.awt.event.ActionListener() { @@ -128,10 +129,17 @@ public final class CasesDashboardTopComponent extends TopComponent implements Ex } }); - org.openide.awt.Mnemonics.setLocalizedText(cleanCaseNodesButton, org.openide.util.NbBundle.getMessage(CasesDashboardTopComponent.class, "CasesDashboardTopComponent.cleanCaseNodesButton.text")); // NOI18N - cleanCaseNodesButton.addActionListener(new java.awt.event.ActionListener() { + org.openide.awt.Mnemonics.setLocalizedText(deleteOrphanCaseNodesButton, org.openide.util.NbBundle.getMessage(CasesDashboardTopComponent.class, "CasesDashboardTopComponent.deleteOrphanCaseNodesButton.text")); // NOI18N + deleteOrphanCaseNodesButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - cleanCaseNodesButtonActionPerformed(evt); + deleteOrphanCaseNodesButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(deleteOrphanManifestNodesButton, org.openide.util.NbBundle.getMessage(CasesDashboardTopComponent.class, "CasesDashboardTopComponent.deleteOrphanManifestNodesButton.text")); // NOI18N + deleteOrphanManifestNodesButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteOrphanManifestNodesButtonActionPerformed(evt); } }); @@ -145,12 +153,17 @@ public final class CasesDashboardTopComponent extends TopComponent implements Ex .addGroup(layout.createSequentialGroup() .addComponent(refreshButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cleanCaseNodesButton) - .addGap(0, 313, Short.MAX_VALUE)) + .addComponent(deleteOrphanCaseNodesButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deleteOrphanManifestNodesButton) + .addGap(0, 0, Short.MAX_VALUE)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addComponent(caseBrowserScrollPane) .addContainerGap()))) ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteOrphanCaseNodesButton, deleteOrphanManifestNodesButton, refreshButton}); + layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() @@ -159,22 +172,31 @@ public final class CasesDashboardTopComponent extends TopComponent implements Ex .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(refreshButton) - .addComponent(cleanCaseNodesButton)) + .addComponent(deleteOrphanCaseNodesButton) + .addComponent(deleteOrphanManifestNodesButton)) .addContainerGap()) ); + + layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {deleteOrphanCaseNodesButton, deleteOrphanManifestNodesButton, refreshButton}); + }// //GEN-END:initComponents private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshButtonActionPerformed caseBrowserPanel.displayCases(); }//GEN-LAST:event_refreshButtonActionPerformed - private void cleanCaseNodesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cleanCaseNodesButtonActionPerformed - new CaseNodesCleanupAction().actionPerformed(evt); - }//GEN-LAST:event_cleanCaseNodesButtonActionPerformed + private void deleteOrphanCaseNodesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteOrphanCaseNodesButtonActionPerformed + new DeleteOrphanCaseNodesAction().actionPerformed(evt); + }//GEN-LAST:event_deleteOrphanCaseNodesButtonActionPerformed + + private void deleteOrphanManifestNodesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteOrphanManifestNodesButtonActionPerformed + new DeleteOrphanManifestNodesAction().actionPerformed(evt); + }//GEN-LAST:event_deleteOrphanManifestNodesButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JScrollPane caseBrowserScrollPane; - private javax.swing.JButton cleanCaseNodesButton; + private javax.swing.JButton deleteOrphanCaseNodesButton; + private javax.swing.JButton deleteOrphanManifestNodesButton; private javax.swing.JButton refreshButton; // End of variables declaration//GEN-END:variables diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesAction.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesAction.java new file mode 100755 index 0000000000..dd4d461d43 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesAction.java @@ -0,0 +1,56 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019-2019 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.experimental.autoingest; + +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.progress.ProgressIndicator; + +/** + * An action class that kicks off a cancellable orphaned case nodes deletion + * task that runs in a background thread and reports progress using an + * application frame progress bar. + */ +final class DeleteOrphanCaseNodesAction extends BackgroundTaskAction { + + private static final long serialVersionUID = 1L; + + /** + * Constructs an instance of an action class that kicks off a cancellable + * orphaned case nodes deletion task that runs in a background thread and + * reports progress using an application frame progress bar. + */ + @NbBundle.Messages({ + "DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes" + }) + DeleteOrphanCaseNodesAction() { + super(Bundle.DeleteOrphanCaseNodesAction_progressDisplayName(), Bundle.DeleteOrphanCaseNodesAction_progressDisplayName()); + } + + @Override + Runnable getTask(ProgressIndicator progress) { + return new DeleteOrphanCaseNodesTask(progress); + } + + @Override + public DeleteOrphanCaseNodesAction clone() throws CloneNotSupportedException { + super.clone(); + throw new CloneNotSupportedException(); + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseNodesCleanupTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesTask.java similarity index 72% rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseNodesCleanupTask.java rename to Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesTask.java index 79addde576..e06427e500 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseNodesCleanupTask.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesTask.java @@ -31,53 +31,53 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.progress.ProgressIndicator; /** - * Task for cleaning up case coordination service nodes for which there is no + * Task for deleting case coordination service nodes for which there is no * longer a corresponding case. */ -final class CaseNodesCleanupTask implements Runnable { +final class DeleteOrphanCaseNodesTask implements Runnable { private static final Logger logger = AutoIngestDashboardLogger.getLogger(); private final ProgressIndicator progress; /** - * Constucts an instance of a task for cleaning up case coordination service + * Constucts an instance of a task for deleting case coordination service * nodes for which there is no longer a corresponding case. * * @param progress */ - CaseNodesCleanupTask(ProgressIndicator progress) { + DeleteOrphanCaseNodesTask(ProgressIndicator progress) { this.progress = progress; } @Override @NbBundle.Messages({ - "CaseNodesCleanupTask.progress.startMessage=Starting orphaned case znode cleanup...", - "CaseNodesCleanupTask.progress.connectingToCoordSvc=Connecting to the coordination service...", - "CaseNodesCleanupTask.progress.gettingCaseNodesListing=Querying coordination service for case nodes..." + "DeleteOrphanCaseNodesTask.progress.startMessage=Starting orphaned case znode cleanup", + "DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service", + "DeleteOrphanCaseNodesTask.progress.gettingCaseNodesListing=Querying coordination service for case znodes" }) public void run() { - progress.start(Bundle.CaseNodesCleanupTask_progress_startMessage()); + progress.start(Bundle.DeleteOrphanCaseNodesTask_progress_startMessage()); try { - progress.progress(Bundle.CaseNodesCleanupTask_progress_connectingToCoordSvc()); - logger.log(Level.INFO, "Connecting to the coordination service for orphan case node clean up"); // NON-NLS + progress.progress(Bundle.DeleteOrphanCaseNodesTask_progress_connectingToCoordSvc()); + logger.log(Level.INFO, Bundle.DeleteOrphanCaseNodesTask_progress_connectingToCoordSvc()); CoordinationService coordinationService; try { coordinationService = CoordinationService.getInstance(); } catch (CoordinationService.CoordinationServiceException ex) { - logger.log(Level.WARNING, "Error connecting to the coordination service", ex); // NON-NLS + logger.log(Level.SEVERE, "Error connecting to the coordination service", ex); //NON-NLS return; } - progress.progress(Bundle.CaseNodesCleanupTask_progress_gettingCaseNodesListing()); - logger.log(Level.INFO, "Querying coordination service for case nodes for orphaned case node clean up"); // NON-NLS + progress.progress(Bundle.DeleteOrphanCaseNodesTask_progress_gettingCaseNodesListing()); + logger.log(Level.INFO, Bundle.DeleteOrphanCaseNodesTask_progress_gettingCaseNodesListing()); List nodeDataList; try { nodeDataList = CaseNodeDataCollector.getNodeData(); } catch (CoordinationService.CoordinationServiceException ex) { - logger.log(Level.WARNING, "Error collecting case node data", ex); // NON-NLS + logger.log(Level.SEVERE, "Error collecting case node data", ex); //NON-NLS return; - } catch (InterruptedException ex) { - logger.log(Level.WARNING, "Unexpected interrupt while collecting case node data", ex); // NON-NLS + } catch (InterruptedException unused) { + logger.log(Level.WARNING, "Task cancelled while collecting case node data"); //NON-NLS return; } @@ -100,8 +100,8 @@ final class CaseNodesCleanupTask implements Runnable { nodePath = CoordinationServiceUtils.getCaseDirectoryNodePath(caseDirectoryPath); deleteNode(coordinationService, caseName, nodePath); - } catch (InterruptedException ex) { - logger.log(Level.WARNING, String.format("Unexpected interrupt while deleting orphaned znode %s for %s", nodePath, caseName), ex); // NON-NLS + } catch (InterruptedException unused) { + logger.log(Level.WARNING, String.format("Task cancelled while deleting orphaned znode %s for %s", nodePath, caseName)); //NON-NLS return; } } @@ -113,9 +113,9 @@ final class CaseNodesCleanupTask implements Runnable { * where there is no call to get() on a Future associated with * the task, so this ensures that any such errors get logged. */ - logger.log(Level.SEVERE, "Unexpected error during orphan case znode cleanup", ex); // NON-NLS + logger.log(Level.SEVERE, "Unexpected error during orphan case znode cleanup", ex); //NON-NLS throw ex; - + } finally { progress.finish(); } @@ -132,16 +132,16 @@ final class CaseNodesCleanupTask implements Runnable { * interrupted during the delete operation. */ @NbBundle.Messages({ - "# {0} - node path", "CaseNodesCleanupTask.progress.deletingOrphanedCaseNode=Deleting orphaned case node {0}..." + "# {0} - node path", "DeleteOrphanCaseNodesTask.progress.deletingOrphanedCaseNode=Deleting orphaned case znode {0}" }) private void deleteNode(CoordinationService coordinationService, String caseName, String nodePath) throws InterruptedException { try { - progress.progress(Bundle.CaseNodesCleanupTask_progress_deletingOrphanedCaseNode(nodePath)); - logger.log(Level.INFO, String.format("Deleting orphaned case node %s for %s", nodePath, caseName)); // NON-NLS + progress.progress(Bundle.DeleteOrphanCaseNodesTask_progress_deletingOrphanedCaseNode(nodePath)); + logger.log(Level.INFO, String.format("Deleting orphaned case node %s for %s", nodePath, caseName)); //NON-NLS coordinationService.deleteNode(CoordinationService.CategoryNode.CASES, nodePath); } catch (CoordinationService.CoordinationServiceException ex) { if (!DeleteCaseUtils.isNoNodeException(ex)) { - logger.log(Level.SEVERE, String.format("Error deleting orphaned case node %s for %s", nodePath, caseName), ex); // NON-NLS + logger.log(Level.SEVERE, String.format("Error deleting orphaned case node %s for %s", nodePath, caseName), ex); //NON-NLS } } } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanManifestNodesAction.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanManifestNodesAction.java new file mode 100755 index 0000000000..e106b7a5de --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanManifestNodesAction.java @@ -0,0 +1,56 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019-2019 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.experimental.autoingest; + +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.progress.ProgressIndicator; + +/** + * An action class that kicks off a cancellable orphaned manifest file nodes + * deletion task that runs in a background thread and reports progress using an + * application frame progress bar. + */ +public class DeleteOrphanManifestNodesAction extends BackgroundTaskAction { + + private static final long serialVersionUID = 1L; + + /** + * Constructs an instance of an action class that kicks off a cancellable + * orphaned manifest file nodes deletion task that runs in a background + * thread and reports progress using an application frame progress bar. + */ + @NbBundle.Messages({ + "DeleteOrphanManifestNodesAction.progressDisplayName=Cleanup Manifest File Znodes" + }) + DeleteOrphanManifestNodesAction() { + super(Bundle.DeleteOrphanManifestNodesAction_progressDisplayName(), Bundle.DeleteOrphanManifestNodesAction_progressDisplayName()); + } + + @Override + Runnable getTask(ProgressIndicator progress) { + return new DeleteOrphanManifestNodesTask(progress); + } + + @Override + public DeleteOrphanManifestNodesAction clone() throws CloneNotSupportedException { + super.clone(); + throw new CloneNotSupportedException(); + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanManifestNodesTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanManifestNodesTask.java new file mode 100755 index 0000000000..158a092522 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanManifestNodesTask.java @@ -0,0 +1,116 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019-2019 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.experimental.autoingest; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; +import java.util.logging.Level; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.progress.ProgressIndicator; + +/** + * A task class for cleaning up auto ingest job coordination service nodes for + * which there is no longer a corresponding manifest file. + */ +final class DeleteOrphanManifestNodesTask implements Runnable { + + private static final Logger logger = Logger.getLogger(DeleteOrphanManifestNodesTask.class.getName()); + private final ProgressIndicator progress; + + /** + * Constucts an instance of a task for cleaning up case coordination service + * nodes for which there is no longer a corresponding case. + * + * @param progress + */ + DeleteOrphanManifestNodesTask(ProgressIndicator progress) { + this.progress = progress; + } + + @Override + @NbBundle.Messages({ + "DeleteOrphanManifestNodesTask.progress.startMessage=Starting orphaned manifest file znode cleanup", + "DeleteOrphanManifestNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service", + "DeleteOrphanManifestNodesTask.progress.gettingManifestNodes=Querying the coordination service for manifest file znodes", + "# {0} - node path", "DeleteOrphanManifestNodesTask.progress.deletingOrphanedManifestNode=Deleting orphaned manifest file znode {0}" + }) + public void run() { + progress.start(Bundle.DeleteOrphanManifestNodesTask_progress_startMessage()); + try { + progress.progress(Bundle.DeleteOrphanManifestNodesTask_progress_connectingToCoordSvc()); + logger.log(Level.INFO, Bundle.DeleteOrphanManifestNodesTask_progress_connectingToCoordSvc()); + CoordinationService coordinationService; + try { + coordinationService = CoordinationService.getInstance(); + } catch (CoordinationService.CoordinationServiceException ex) { + logger.log(Level.SEVERE, "Error connecting to the coordination service", ex); // NON-NLS + return; + } + + progress.progress(Bundle.DeleteOrphanManifestNodesTask_progress_gettingManifestNodes()); + logger.log(Level.INFO, Bundle.DeleteOrphanManifestNodesTask_progress_gettingManifestNodes()); + List nodeDataList; + try { + nodeDataList = AutoIngestJobNodeDataCollector.getNodeData(); + } catch (CoordinationService.CoordinationServiceException ex) { + logger.log(Level.SEVERE, "Error collecting auto ingest job node data", ex); // NON-NLS + return; + } catch (InterruptedException unused) { + logger.log(Level.WARNING, "Task cancelled while collecting auto ingest job node data"); // NON-NLS + return; + } + + for (AutoIngestJobNodeData nodeData : nodeDataList) { + final String caseName = nodeData.getCaseName(); + final Path manifestFilePath = nodeData.getManifestFilePath(); + final File manifestFile = manifestFilePath.toFile(); + if (!manifestFile.exists()) { + try { + progress.progress(Bundle.DeleteOrphanManifestNodesTask_progress_deletingOrphanedManifestNode(manifestFilePath)); + logger.log(Level.INFO, String.format("Deleting orphaned manifest file znode %s for %s", manifestFilePath, caseName)); + coordinationService.deleteNode(CoordinationService.CategoryNode.MANIFESTS, manifestFilePath.toString()); + } catch (CoordinationService.CoordinationServiceException ex) { + if (!DeleteCaseUtils.isNoNodeException(ex)) { + logger.log(Level.SEVERE, String.format("Error deleting %s znode for %s", manifestFilePath, caseName), ex); // NON-NLS + } + } catch (InterruptedException unused) { + logger.log(Level.WARNING, String.format("Task cancelled while deleting %s znode for %s", manifestFilePath, caseName)); // NON-NLS + return; + } + } + } + } catch (Exception ex) { + /* + * This is an unexpected runtime exceptions firewall. It is here + * because this task is designed to be able to be run in scenarios + * where there is no call to get() on a Future associated with + * the task, so this ensures that any such errors get logged. + */ + logger.log(Level.SEVERE, "Unexpected error deleting orphan manifest file znodes", ex); // NON-NLS + throw ex; + + } finally { + progress.finish(); + } + } + +}