mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 18:17:43 +00:00
Interim check in of adminstrative case deletion
This commit is contained in:
parent
ad227a3db5
commit
486fbfe152
@ -11,7 +11,7 @@ Case.deleteCaseFailureMessageBox.message=Error deleting case: {0}
|
||||
Case.deleteCaseFailureMessageBox.title=Failed to Delete Case
|
||||
Case.exceptionMessage.cancelledByUser=Cancelled by user.
|
||||
Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.
|
||||
Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user.
|
||||
Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.
|
||||
Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window
|
||||
Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case.
|
||||
# {0} - exception message
|
||||
@ -34,7 +34,7 @@ Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case datab
|
||||
Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}.
|
||||
Case.exceptionMessage.emptyCaseDir=Must specify a case directory path.
|
||||
Case.exceptionMessage.emptyCaseName=Must specify a case name.
|
||||
Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details.
|
||||
Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the log for details.
|
||||
# {0} - exception message
|
||||
Case.exceptionMessage.execExceptionWrapperMessage={0}
|
||||
# {0} - exception message
|
||||
@ -58,36 +58,12 @@ Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...
|
||||
Case.progressMessage.creatingCaseDatabase=Creating case database...
|
||||
Case.progressMessage.creatingCaseDirectory=Creating case directory...
|
||||
Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...
|
||||
Case.progressMessage.deletingAutoIngestLogCoordSvcNode=Deleting case auto ingest log lock coordination service node...
|
||||
Case.progressMessage.deletingCaseDatabase=Deleting case database...
|
||||
Case.progressMessage.deletingCaseDirectory=Deleting case directory...
|
||||
Case.progressMessage.deletingDirectoryCoordinationServiceNode=Deleting case directory lock coordination service node...
|
||||
Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resourceslock coordination service node...
|
||||
Case.progressMessage.deletingCaseDirLockNode=Deleting case directory lock coordination service node...
|
||||
Case.progressMessage.deletingResourcesLockNode=Deleting case resources lock node...
|
||||
Case.progressMessage.deletingTextIndex=Deleting text index...
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorConnectingToCoordSvc=An error occurred connecting to the coordination service (see log for details): {0}.
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorDeletingCaseDb=An error occurred deleting the case database (see log for details): {0}.
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorDeletingCaseDir=An error occurred deleting the case directory (see log for details): {0}.
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorDeletingCaseDirLockNode=An error occurred deleting the case directory lock node (see log for details): {0}.
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorDeletingJobLogLockNode=An error occurred deleting the case auto ingest log lock node (see log for details): {0}.
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorDeletingResourcesLockNode=An error occurred deleting the case resources lock node (see log for details): {0}.
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorDeletingTextIndex=An error occurred deleting the text index for the case (see log for details): {0}.
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorGettingCoordSvcNodeData=Failed to get the coordination service case node data (see log for details): {0}.
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorLockingCase=An error occurred exclusively locking the case for deletion (see log for details): {0}.
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorLockingCaseName=An error occurred exclusively locking the case name for deletion (see log for details): {0}.
|
||||
# {0} - exception message
|
||||
Case.progressMessage.errorSavingDeletedItemsFlags=An error occurred saving the deleted items flags in the coordination service database (see log for details): {0}.
|
||||
Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case...
|
||||
Case.progressMessage.missingCoordSvcNodeData=Missing coordination service node data.
|
||||
Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...
|
||||
Case.progressMessage.openingCaseDatabase=Opening case database...
|
||||
Case.progressMessage.openingCaseLevelServices=Opening case-level services...
|
||||
|
@ -702,8 +702,45 @@ public class Case {
|
||||
}
|
||||
CaseMetadata metadata = currentCase.getMetadata();
|
||||
closeCurrentCase();
|
||||
ProgressIndicator progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
|
||||
deleteCase(metadata, true, progressIndicator);
|
||||
deleteCase(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a case. The case to be deleted must not be the "current case."
|
||||
*
|
||||
* @param metadata The case metadata.
|
||||
*
|
||||
* @throws CaseActionException If there were one or more errors deleting the
|
||||
* case. The exception will have a user-friendly
|
||||
* message and may be a wrapper for a
|
||||
* lower-level exception.
|
||||
*/
|
||||
@Messages({
|
||||
"Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first."
|
||||
})
|
||||
public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
|
||||
synchronized (caseActionSerializationLock) {
|
||||
if (null != currentCase) {
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
|
||||
}
|
||||
}
|
||||
|
||||
ProgressIndicator progressIndicator;
|
||||
if (RuntimeProperties.runningWithGUI()) {
|
||||
progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
|
||||
} else {
|
||||
progressIndicator = new LoggingProgressIndicator();
|
||||
}
|
||||
progressIndicator.start(Bundle.Case_progressMessage_preparing());
|
||||
try {
|
||||
if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
|
||||
deleteSingleUserCase(metadata, progressIndicator);
|
||||
} else {
|
||||
deleteMultiUserCase(metadata, progressIndicator);
|
||||
}
|
||||
} finally {
|
||||
progressIndicator.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@ -884,40 +921,6 @@ public class Case {
|
||||
return imgPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a case. The case to be deletd must not be the "current case."
|
||||
*
|
||||
* @param metadata The case metadata.
|
||||
* @param deleteCaseLockNodes Whether or not to delete the coordination
|
||||
* service lock nodes for the case.
|
||||
* @param progressIndicator A progress indicator.
|
||||
*
|
||||
* @throws CaseActionException If there were one or more errors deleting the
|
||||
* case.
|
||||
*/
|
||||
@Beta
|
||||
@Messages({
|
||||
"Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first."
|
||||
})
|
||||
public static void deleteCase(CaseMetadata metadata, boolean deleteCaseLockNodes, ProgressIndicator progressIndicator) throws CaseActionException {
|
||||
synchronized (caseActionSerializationLock) {
|
||||
if (null != currentCase) {
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
|
||||
}
|
||||
}
|
||||
|
||||
progressIndicator.start(Bundle.Case_progressMessage_preparing());
|
||||
try {
|
||||
if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
|
||||
deleteSingleUserCase(metadata, progressIndicator);
|
||||
} else {
|
||||
deleteMultiUserCase(metadata, progressIndicator);
|
||||
}
|
||||
} finally {
|
||||
progressIndicator.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a single-user case.
|
||||
*
|
||||
@ -925,12 +928,12 @@ public class Case {
|
||||
* @param progressIndicator A progress indicator.
|
||||
*
|
||||
* @throws CaseActionException If there were one or more errors deleting the
|
||||
* case.
|
||||
* case. The exception will have a user-friendly
|
||||
* message and may be a wrapper for a
|
||||
* lower-level exception.
|
||||
*/
|
||||
@Messages({
|
||||
"Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details.",
|
||||
"# {0} - exception message", "Case.progressMessage.errorDeletingTextIndex=An error occurred deleting the text index for the case (see log for details): {0}.",
|
||||
"# {0} - exception message", "Case.progressMessage.errorDeletingCaseDir=An error occurred deleting the case directory (see log for details): {0}."
|
||||
"Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the log for details."
|
||||
})
|
||||
private static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
|
||||
boolean errorsOccurred = false;
|
||||
@ -938,16 +941,14 @@ public class Case {
|
||||
deleteTextIndex(metadata, progressIndicator);
|
||||
} catch (KeywordSearchServiceException ex) {
|
||||
errorsOccurred = true;
|
||||
logger.log(Level.SEVERE, String.format("Failed to delete text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorDeletingTextIndex(ex.getMessage()));
|
||||
logger.log(Level.WARNING, String.format("Failed to delete text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
}
|
||||
|
||||
try {
|
||||
deleteCaseDirectory(metadata, progressIndicator);
|
||||
} catch (CaseActionException ex) {
|
||||
errorsOccurred = true;
|
||||
logger.log(Level.SEVERE, String.format("Failed to delete case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorDeletingCaseDir(ex.getMessage()));
|
||||
logger.log(Level.WARNING, String.format("Failed to delete case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
}
|
||||
|
||||
deleteFromRecentCases(metadata, progressIndicator);
|
||||
@ -960,173 +961,184 @@ public class Case {
|
||||
/**
|
||||
* Deletes a multi-user case.
|
||||
*
|
||||
* @param metadata The case metadata.
|
||||
* @param progressIndicator A progress indicator.
|
||||
* @param metadata The case metadata.
|
||||
* @param deleteCaseLockNodes Whether or not to delete the coordination
|
||||
* service case directory and case name lock
|
||||
* nodes for the case.
|
||||
* @param progressIndicator A progress indicator.
|
||||
*
|
||||
* @throws CaseActionException If there were one or more errors deleting the
|
||||
* case.
|
||||
* case. The exception will have a user-friendly
|
||||
* message and may be a wrapper for a
|
||||
* lower-level exception.
|
||||
*/
|
||||
@Messages({
|
||||
"Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...",
|
||||
"# {0} - exception message", "Case.progressMessage.errorConnectingToCoordSvc=An error occurred connecting to the coordination service (see log for details): {0}.",
|
||||
"Case.progressMessage.checkingForOtherUser=Checking to see if another user has the case open...",
|
||||
"Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user.",
|
||||
"# {0} - exception message", "Case.progressMessage.errorLockingCaseName=An error occurred exclusively locking the case name for deletion (see log for details): {0}.",
|
||||
"# {0} - exception message", "Case.progressMessage.errorLockingCase=An error occurred exclusively locking the case for deletion (see log for details): {0}.",
|
||||
"Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case...",
|
||||
"# {0} - exception message", "Case.progressMessage.errorGettingCoordSvcNodeData=Failed to get the coordination service case node data (see log for details): {0}.",
|
||||
"Case.progressMessage.missingCoordSvcNodeData=Missing coordination service node data.",
|
||||
"# {0} - exception message", "Case.progressMessage.errorDeletingCaseDb=An error occurred deleting the case database (see log for details): {0}.",
|
||||
"# {0} - exception message", "Case.progressMessage.errorSavingDeletedItemsFlags=An error occurred saving the deleted items flags in the coordination service database (see log for details): {0}."
|
||||
"Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.",
|
||||
"Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case..."
|
||||
})
|
||||
private static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
|
||||
CaseNodeData caseNodeData;
|
||||
boolean errorsOccurred = false;
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_connectingToCoordSvc());
|
||||
CoordinationService coordinationService;
|
||||
try {
|
||||
coordinationService = CoordinationService.getInstance();
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to connect to coordination service when attempting to delete %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorConnectingToCoordSvc(ex.getMessage()));
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
||||
}
|
||||
|
||||
/*
|
||||
* Acquire an exclusive case name lock. This will prevent auto ingest
|
||||
* nodes from attempting to search for the case directory before it is
|
||||
* deleted.
|
||||
*/
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_checkingForOtherUser());
|
||||
final String caseNameLockName = TimeStampUtils.removeTimeStamp(metadata.getCaseName());
|
||||
try (CoordinationService.Lock nameLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseNameLockName)) {
|
||||
if (nameLock == null) {
|
||||
logger.log(Level.INFO, String.format("Could not delete %s (%s) in %s because the case name was in use by another host", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
|
||||
progressIndicator.progress(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
|
||||
CaseNodeData caseNodeData;
|
||||
boolean errorsOccurred = false;
|
||||
try (CoordinationService.Lock dirLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) {
|
||||
if (dirLock == null) {
|
||||
logger.log(Level.INFO, String.format("Could not delete %s (%s) in %s because a case directory lock was held by another host", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
|
||||
}
|
||||
|
||||
/*
|
||||
* Acquire an exclusive case directory lock. This ensures that no
|
||||
* other node (host) currently has the case open and prevents
|
||||
* another node (host) from trying to open the case as it is being
|
||||
* deleted.
|
||||
*/
|
||||
try (CoordinationService.Lock dirLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) {
|
||||
if (dirLock == null) {
|
||||
logger.log(Level.INFO, String.format("Could not delete %s (%s) in %s because the case was in use by another host", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
|
||||
progressIndicator.progress(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
|
||||
}
|
||||
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
|
||||
try {
|
||||
byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, metadata.getCaseDirectory());
|
||||
if (nodeBytes != null && nodeBytes.length > 0) {
|
||||
caseNodeData = new CaseNodeData(nodeBytes);
|
||||
} else {
|
||||
logger.log(Level.SEVERE, String.format("Missing coordination service node data %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_missingCoordSvcNodeData());
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
||||
}
|
||||
} catch (CoordinationServiceException | InterruptedException | IOException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to get coordination service node data %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorGettingCoordSvcNodeData(ex.getMessage()));
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
|
||||
try {
|
||||
byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, metadata.getCaseDirectory());
|
||||
if (nodeBytes != null && nodeBytes.length > 0) {
|
||||
caseNodeData = new CaseNodeData(nodeBytes);
|
||||
} else {
|
||||
logger.log(Level.WARNING, String.format("Missing coordination service node data %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
||||
}
|
||||
|
||||
try {
|
||||
deleteCaseDatabase(metadata, progressIndicator, caseNodeData);
|
||||
} catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
|
||||
errorsOccurred = true;
|
||||
logger.log(Level.SEVERE, String.format("Failed to delete the case database for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorDeletingCaseDb(ex.getMessage()));
|
||||
}
|
||||
|
||||
try {
|
||||
deleteTextIndex(metadata, progressIndicator, caseNodeData);
|
||||
} catch (KeywordSearchServiceException ex) {
|
||||
errorsOccurred = true;
|
||||
logger.log(Level.SEVERE, String.format("Failed to delete the text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorDeletingTextIndex(ex.getMessage()));
|
||||
}
|
||||
|
||||
try {
|
||||
deleteCaseDirectory(metadata, progressIndicator, caseNodeData);
|
||||
} catch (CaseActionException ex) {
|
||||
errorsOccurred = true;
|
||||
logger.log(Level.SEVERE, String.format("Failed to delete the case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorDeletingCaseDir(ex.getMessage()));
|
||||
}
|
||||
|
||||
deleteFromRecentCases(metadata, progressIndicator);
|
||||
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to exclusively lock the case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorLockingCase(ex.getMessage()));
|
||||
} catch (CoordinationServiceException | InterruptedException | IOException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to get coordination service node data %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
||||
}
|
||||
|
||||
errorsOccurred = deleteMultiUserCase(caseNodeData, metadata, progressIndicator);
|
||||
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to exclusively lock the case name for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorLockingCaseName(ex.getMessage()));
|
||||
logger.log(Level.SEVERE, String.format("Error exclusively locking the case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
||||
}
|
||||
|
||||
deleteCaseResourcesCoordinationServiceNode(metadata, progressIndicator, coordinationService);
|
||||
deleteCaseAutoIngestLogCoordinationServiceNode(metadata, progressIndicator, coordinationService);
|
||||
// RJCTODO: Delete case name lock node
|
||||
|
||||
if (!errorsOccurred) {
|
||||
deleteCaseDirectoryCoordinationServiceNode(metadata, progressIndicator, coordinationService);
|
||||
} else {
|
||||
/*
|
||||
* Save the deleted item flags for the partially deleted case so
|
||||
* that system adminstrators can try to fix whatever wnet wrong and
|
||||
* then retry deleting the case.
|
||||
*/
|
||||
try {
|
||||
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), caseNodeData.toArray());
|
||||
} catch (IOException | CoordinationServiceException | InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to write the node dat with the deleted items flags for the partially deleted case at %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorSavingDeletedItemsFlags(ex.getMessage()));
|
||||
deleteCaseDirectoryLockNode(caseNodeData, progressIndicator);
|
||||
} catch (CoordinationServiceException | InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error deleting the case directory lock node for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
errorsOccurred = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorsOccurred) {
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to delete the case database for a multi-user case.
|
||||
* IMPORTANT: This is a "beta" method and is subject to change or removal
|
||||
* without notice!
|
||||
*
|
||||
* Attempts to delete the case database, the text index, the case directory,
|
||||
* and the case resources coordination service lock code for a case and
|
||||
* removes the case from the recent cases menu of the mian application
|
||||
* window.
|
||||
*
|
||||
* @param caseNodeData The coordination service node data for the case.
|
||||
* @param metadata The case metadata.
|
||||
* @param progressIndicator A progress indicator.
|
||||
* @param caseNodeData The coordination service node data for the case.
|
||||
*
|
||||
* @throws UserPreferencesException If there is an error getting the
|
||||
* @return True if one or more errors occurred (see log for details), false
|
||||
* otherwise.
|
||||
*
|
||||
* @throws InterruptedException If the thread this code is running in is
|
||||
* interrupted while blocked, i.e., if
|
||||
* cancellation of the opersation is detected
|
||||
* during a wait.
|
||||
*/
|
||||
@Beta
|
||||
public static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator) throws InterruptedException {
|
||||
boolean errorsOccurred = false;
|
||||
try {
|
||||
deleteCaseDatabase(caseNodeData, metadata, progressIndicator);
|
||||
} catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
|
||||
errorsOccurred = true;
|
||||
logger.log(Level.WARNING, String.format("Failed to delete the case database for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return errorsOccurred;
|
||||
}
|
||||
|
||||
try {
|
||||
deleteTextIndex(caseNodeData, metadata, progressIndicator);
|
||||
} catch (KeywordSearchServiceException ex) {
|
||||
errorsOccurred = true;
|
||||
logger.log(Level.WARNING, String.format("Failed to delete the text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return errorsOccurred;
|
||||
}
|
||||
|
||||
try {
|
||||
deleteCaseDirectory(caseNodeData, metadata, progressIndicator);
|
||||
} catch (CaseActionException ex) {
|
||||
errorsOccurred = true;
|
||||
logger.log(Level.WARNING, String.format("Failed to delete the case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return errorsOccurred;
|
||||
}
|
||||
|
||||
deleteFromRecentCases(metadata, progressIndicator);
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
Thread.currentThread().interrupt();
|
||||
return errorsOccurred;
|
||||
}
|
||||
|
||||
try {
|
||||
deleteCaseResourcesLockNode(caseNodeData, progressIndicator);
|
||||
} catch (CoordinationServiceException ex) {
|
||||
errorsOccurred = true;
|
||||
logger.log(Level.WARNING, String.format("Error deleting the case resources lock coordination service node for the case at %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return errorsOccurred;
|
||||
}
|
||||
|
||||
return errorsOccurred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to delete the case database for a multi-user case.
|
||||
*
|
||||
* @param caseNodeData The coordination service node data for the case.
|
||||
* @param metadata The case metadata.
|
||||
* @param progressIndicator A progress indicator.
|
||||
*
|
||||
* @throws UserPreferencesException if there is an error getting the
|
||||
* database server connection info.
|
||||
* @throws ClassNotFoundException If there is an error gettting the
|
||||
* @throws ClassNotFoundException if there is an error gettting the
|
||||
* required JDBC driver.
|
||||
* @throws SQLException If there is an error executing the SQL
|
||||
* @throws SQLException if there is an error executing the SQL
|
||||
* to drop the database from the database
|
||||
* server.
|
||||
* @throws InterruptedException If interrupted while blocked waiting for
|
||||
* coordination service data to be written
|
||||
* to the coordination service node
|
||||
* database.
|
||||
*/
|
||||
@Messages({
|
||||
"Case.progressMessage.deletingCaseDatabase=Deleting case database...",})
|
||||
private static void deleteCaseDatabase(CaseMetadata metadata, ProgressIndicator progressIndicator,
|
||||
CaseNodeData caseNodeData) throws UserPreferencesException, ClassNotFoundException, SQLException {
|
||||
if (caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DB)) {
|
||||
return;
|
||||
"Case.progressMessage.deletingCaseDatabase=Deleting case database..."
|
||||
})
|
||||
private static void deleteCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator) throws UserPreferencesException, ClassNotFoundException, SQLException, InterruptedException {
|
||||
if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DB)) {
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
|
||||
CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo();
|
||||
String url = "jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres"; //NON-NLS
|
||||
Class.forName("org.postgresql.Driver"); //NON-NLS
|
||||
try (Connection connection = DriverManager.getConnection(url, info.getUserName(), info.getPassword()); Statement statement = connection.createStatement()) {
|
||||
String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
|
||||
statement.execute(deleteCommand);
|
||||
}
|
||||
setDeletedItemFlag(caseNodeData, CaseNodeData.DeletedFlags.CASE_DB);
|
||||
}
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
|
||||
CaseDbConnectionInfo db;
|
||||
db = UserPreferences.getDatabaseConnectionInfo();
|
||||
Class.forName("org.postgresql.Driver"); //NON-NLS
|
||||
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + db.getHost() + ":" + db.getPort() + "/postgres", db.getUserName(), db.getPassword()); //NON-NLS
|
||||
Statement statement = connection.createStatement();) {
|
||||
String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
|
||||
statement.execute(deleteCommand);
|
||||
}
|
||||
caseNodeData.setDeletedFlag(CaseNodeData.DeletedFlags.CASE_DB);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1151,19 +1163,21 @@ public class Case {
|
||||
/**
|
||||
* Attempts to delete the text index for a multi-user case.
|
||||
*
|
||||
* @param caseNodeData The coordination service node data for the case.
|
||||
* @param metadata The case mnetadata.
|
||||
* @param progressIndicator A progress indicator.
|
||||
* @param caseNodeData The coordination service node data for the case
|
||||
* if it is a multi-user case; for a single-user
|
||||
* case pass null.
|
||||
*
|
||||
* @throws KeywordSearchServiceException If there is an error deleting the
|
||||
* text index.
|
||||
* @throws InterruptedException If interrupted while blocked
|
||||
* waiting for coordination service
|
||||
* data to be written to the
|
||||
* coordination service node database.
|
||||
*/
|
||||
private static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator, CaseNodeData caseNodeData) throws KeywordSearchServiceException {
|
||||
private static void deleteTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator) throws KeywordSearchServiceException, InterruptedException {
|
||||
if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.TEXT_INDEX)) {
|
||||
deleteTextIndex(metadata, progressIndicator);
|
||||
caseNodeData.setDeletedFlag(CaseNodeData.DeletedFlags.TEXT_INDEX);
|
||||
setDeletedItemFlag(caseNodeData, CaseNodeData.DeletedFlags.TEXT_INDEX);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1180,27 +1194,32 @@ public class Case {
|
||||
"Case.progressMessage.deletingCaseDirectory=Deleting case directory..."
|
||||
})
|
||||
private static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
|
||||
// RJCTODO: Update FileUtil.deleteDir to use robocopy on Windows
|
||||
// when the path is >= 255 chars. Actually, deprecate this method and
|
||||
// replace it with one that throws instead of returning a boolean value.
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
|
||||
// RJCTODO: Use robocopy on Windows, possibly just for longer paths
|
||||
if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
|
||||
throw new CaseActionException(String.format("Failed to delete %s", metadata.getCaseDirectory()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to delete the case directory for a case.
|
||||
* Attempts to delete the case directory for a multi-user case.
|
||||
*
|
||||
* @param caseNodeData The coordination service node data for the case.
|
||||
* @param metadata The case mnetadata.
|
||||
* @param progressIndicator A progress indicator.
|
||||
* @param caseNodeData The coordination service node data for the case.
|
||||
*
|
||||
* @throws CaseActionException If there is an error deleting the case
|
||||
* directory.
|
||||
* @throws CaseActionException if there is an error deleting the case
|
||||
* directory.
|
||||
* @throws InterruptedException If interrupted while blocked waiting for
|
||||
* coordination service data to be written to
|
||||
* the coordination service node database.
|
||||
*/
|
||||
private static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator, CaseNodeData caseNodeData) throws CaseActionException {
|
||||
private static void deleteCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException, InterruptedException {
|
||||
if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DIR)) {
|
||||
deleteCaseDirectory(metadata, progressIndicator);
|
||||
caseNodeData.setDeletedFlag(CaseNodeData.DeletedFlags.CASE_DIR);
|
||||
setDeletedItemFlag(caseNodeData, CaseNodeData.DeletedFlags.CASE_DIR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1223,71 +1242,82 @@ public class Case {
|
||||
}
|
||||
}
|
||||
|
||||
// RJCTODO: Copy-paste instead
|
||||
/**
|
||||
* Deletes the case resources coordination nodes for a multi-user case.
|
||||
* IMPORTANT: This is a "beta" method and is subject to change or removal
|
||||
* without notice!
|
||||
*
|
||||
* @param metadata The case metadata.
|
||||
* @param progressIndicator A progress indicator.
|
||||
*/
|
||||
@Messages({
|
||||
"Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resourceslock coordination service node...",
|
||||
"# {0} - exception message", "Case.progressMessage.errorDeletingResourcesLockNode=An error occurred deleting the case resources lock node (see log for details): {0}."
|
||||
})
|
||||
static void deleteCaseResourcesCoordinationServiceNode(CaseMetadata metadata, ProgressIndicator progressIndicator, CoordinationService coordinationService) {
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_deletingResourcesCoordSvcNode());
|
||||
String resourcesLockNodePath = metadata.getCaseDirectory() + RESOURCES_LOCK_SUFFIX;
|
||||
try {
|
||||
coordinationService.deleteNode(CategoryNode.CASES, resourcesLockNodePath);
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error deleting the case resources lock coordination service node for the case at %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorDeletingResourcesLockNode(ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the case auto ingest log lock coordination service node for a
|
||||
* multi-user case, if it exists.
|
||||
*
|
||||
* @param metadata The case metadata.
|
||||
* @param progressIndicator A progress indicator.
|
||||
*/
|
||||
@Messages({
|
||||
"Case.progressMessage.deletingAutoIngestLogCoordSvcNode=Deleting case auto ingest log lock coordination service node...",
|
||||
"# {0} - exception message", "Case.progressMessage.errorDeletingJobLogLockNode=An error occurred deleting the case auto ingest log lock node (see log for details): {0}."
|
||||
})
|
||||
static void deleteCaseAutoIngestLogCoordinationServiceNode(CaseMetadata metadata, ProgressIndicator progressIndicator, CoordinationService coordinationService) {
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_deletingAutoIngestLogCoordSvcNode());
|
||||
Path logFilePath = Paths.get(metadata.getCaseDirectory(), AUTO_INGEST_LOG_FILE_NAME);
|
||||
try {
|
||||
/*
|
||||
* Does nothing if the node does not exist.
|
||||
*/
|
||||
coordinationService.deleteNode(CategoryNode.CASES, logFilePath.toString());
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error deleting the case auto ingest log lock coordination service node for the case at %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorDeletingJobLogLockNode(ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the case (directory lock) coordination service node for a
|
||||
* Deletes the case resources lock coordination service node for a
|
||||
* multi-user case.
|
||||
*
|
||||
* @param metadata The metadata for the case to delete.
|
||||
* @param caseNodeData The coordination service node data for the case.
|
||||
* @param progressIndicator The progress indicator for the deletion
|
||||
* operation.
|
||||
*
|
||||
* @throws CoordinationServiceException If there is an error deleting the
|
||||
* coordination service node.
|
||||
* @throws InterruptedException If a thread interrupt occurs while
|
||||
* blocked waiting for the operation to
|
||||
* complete.
|
||||
*/
|
||||
@Messages({
|
||||
"Case.progressMessage.deletingDirectoryCoordinationServiceNode=Deleting case directory lock coordination service node...",
|
||||
"# {0} - exception message", "Case.progressMessage.errorDeletingCaseDirLockNode=An error occurred deleting the case directory lock node (see log for details): {0}."
|
||||
"Case.progressMessage.deletingResourcesLockNode=Deleting case resources lock node..."
|
||||
})
|
||||
static void deleteCaseDirectoryCoordinationServiceNode(CaseMetadata metadata, ProgressIndicator progressIndicator, CoordinationService coordinationService) {
|
||||
String caseDirectoryLockNodePath = metadata.getCaseDirectory();
|
||||
@Beta
|
||||
private static void deleteCaseResourcesLockNode(CaseNodeData caseNodeData, ProgressIndicator progressIndicator) throws CoordinationServiceException, InterruptedException {
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_deletingResourcesLockNode());
|
||||
String resourcesLockNodePath = caseNodeData.getDirectory().toString() + RESOURCES_LOCK_SUFFIX;//RJCTODO: Use utility
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
coordinationService.deleteNode(CategoryNode.CASES, resourcesLockNodePath);
|
||||
}
|
||||
|
||||
// RJCTODO: Copy-paste instead
|
||||
/**
|
||||
* IMPORTANT: This is a "beta" method and is subject to change or removal
|
||||
* without notice!
|
||||
*
|
||||
* Deletes the case directory lock coordination service node for a
|
||||
* multi-user case.
|
||||
*
|
||||
* @param caseNodeData The coordination service node data for the case.
|
||||
* @param progressIndicator The progress indicator for the deletion
|
||||
* operation.
|
||||
*
|
||||
* @throws CoordinationServiceException If there is an error deleting the
|
||||
* coordination service node.
|
||||
* @throws InterruptedException If a thread interrupt occurs while
|
||||
* blocked waiting for the operation to
|
||||
* complete.
|
||||
*/
|
||||
@Beta
|
||||
@Messages({
|
||||
"Case.progressMessage.deletingCaseDirLockNode=Deleting case directory lock coordination service node..."
|
||||
})
|
||||
public static void deleteCaseDirectoryLockNode(CaseNodeData caseNodeData, ProgressIndicator progressIndicator) throws CoordinationServiceException, InterruptedException {
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirLockNode());
|
||||
String caseDirectoryLockNodePath = caseNodeData.getDirectory().toString();
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
coordinationService.deleteNode(CategoryNode.CASES, caseDirectoryLockNodePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a deleted item flag in the coordination service node data for a
|
||||
* multi-user case.
|
||||
*
|
||||
* @param caseNodeData The coordination service node data for the case.
|
||||
* @param flag The flag to set.
|
||||
*
|
||||
* @throws InterruptedException If interrupted while blocked waiting for
|
||||
* coordination service data to be written to
|
||||
* the coordination service node database.
|
||||
*/
|
||||
private static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag) throws InterruptedException {
|
||||
try {
|
||||
coordinationService.deleteNode(CategoryNode.CASES, caseDirectoryLockNodePath);
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error deleting the case directory lock coordination service node for the case at %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_errorDeletingCaseDirLockNode(ex.getMessage()));
|
||||
caseNodeData.setDeletedFlag(flag);
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
coordinationService.setNodeData(CategoryNode.CASES, caseNodeData.getDirectory().toString(), caseNodeData.toArray());
|
||||
} catch (IOException | CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error updating deleted item flag %s for %s (%s) in %s", flag.name(), caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3310,31 +3340,4 @@ public class Case {
|
||||
deleteReports(reports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a case.
|
||||
*
|
||||
* @param metadata The metadata for the case to delete.
|
||||
*
|
||||
* @throws CaseActionException If there is a problem deleting the case. The
|
||||
* exception will have a user-friendly message
|
||||
* and may be a wrapper for a lower-level
|
||||
* exception.
|
||||
* @deprecated Use deleteCase(CaseMetadata, ProgressIndicator) instead.
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
|
||||
/*
|
||||
* Set up either a GUI progress indicator without a cancel button (can't
|
||||
* cancel deleting a case) or a logging progress indicator.
|
||||
*/
|
||||
ProgressIndicator progressIndicator;
|
||||
if (RuntimeProperties.runningWithGUI()) {
|
||||
progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
|
||||
} else {
|
||||
progressIndicator = new LoggingProgressIndicator();
|
||||
}
|
||||
deleteCase(metadata, true, progressIndicator);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ public final class CaseNodeData {
|
||||
CASE_DB(2),
|
||||
CASE_DIR(4),
|
||||
DATA_SOURCES(8),
|
||||
JOB_MANIFEST_NODES(16);
|
||||
MANIFEST_FILE_LOCK_NODES(16);
|
||||
|
||||
private final short value;
|
||||
|
||||
|
@ -29,76 +29,60 @@ import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Queries the coordination service to collect the multi-user case node data
|
||||
* stored in the case directory lock ZooKeeper nodes.
|
||||
*/
|
||||
final public class MultiUserCaseNodeDataCollector {
|
||||
final public class MultiUserCaseNodeDataCollector { // RJCTODO: Shorten name after multi-case keyword search code is in.
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MultiUserCaseNodeDataCollector.class.getName());
|
||||
private static final String CASE_AUTO_INGEST_LOG_NAME = "AUTO_INGEST_LOG.TXT"; //NON-NLS
|
||||
private static final String RESOURCES_LOCK_SUFFIX = "_RESOURCES"; //NON-NLS
|
||||
|
||||
/**
|
||||
* Queries the coordination service to collect the multi-user case node data
|
||||
* stored in the case directory lock ZooKeeper nodes.
|
||||
*
|
||||
* @return A list of CaseNodedata objects that convert data for a case
|
||||
* directory lock coordination service node to and from byte arrays.
|
||||
* @return The node data for the multi-user cases known to the coordination
|
||||
* service.
|
||||
*
|
||||
* @throws CoordinationServiceException If there is an error
|
||||
* @throws CoordinationServiceException If there is an error interacting
|
||||
* with the coordination service.
|
||||
* @throws InterruptedException If the current thread is interrupted
|
||||
* while waiting for the coordination
|
||||
* service.
|
||||
*/
|
||||
public static List<CaseNodeData> getNodeData() throws CoordinationService.CoordinationServiceException {
|
||||
public static List<CaseNodeData> getNodeData() throws CoordinationServiceException, InterruptedException {
|
||||
final List<CaseNodeData> cases = new ArrayList<>();
|
||||
final CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
final List<String> nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
|
||||
for (String nodeName : nodeList) {
|
||||
/*
|
||||
* Ignore auto ingest case name lock nodes.
|
||||
*/
|
||||
final Path nodeNameAsPath = Paths.get(nodeName);
|
||||
if (!(nodeNameAsPath.toString().contains("\\") || nodeNameAsPath.toString().contains("//"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore case auto ingest log lock nodes and resource lock nodes.
|
||||
*/
|
||||
final String lastNodeNameComponent = nodeNameAsPath.getFileName().toString();
|
||||
if (lastNodeNameComponent.equals(CASE_AUTO_INGEST_LOG_NAME)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore case resources lock nodes.
|
||||
*/
|
||||
if (lastNodeNameComponent.endsWith(RESOURCES_LOCK_SUFFIX)) {
|
||||
if (CaseCoordinationServiceUtils.isCaseLockName(nodeName)
|
||||
|| CaseCoordinationServiceUtils.isCaseResourcesLockName(nodeName)
|
||||
|| CaseCoordinationServiceUtils.isCaseAutoIngestLogLockName(nodeName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the data from the case directory lock node. This data may not
|
||||
* exist for "legacy" nodes. If it is missing, create it.
|
||||
* exist or may exist only in an older version. If it is missing or
|
||||
* incomplete, create or update it.
|
||||
*/
|
||||
try {
|
||||
CaseNodeData nodeData;
|
||||
byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodeName);
|
||||
final byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodeName);
|
||||
if (nodeBytes != null && nodeBytes.length > 0) {
|
||||
nodeData = new CaseNodeData(nodeBytes);
|
||||
if (nodeData.getVersion() == 0) {
|
||||
/*
|
||||
* Version 0 case node data was only written if errors
|
||||
* occurred during an auto ingest job and consisted of
|
||||
* only the set errors flag.
|
||||
*/
|
||||
nodeData = createNodeDataFromCaseMetadata(nodeName, true);
|
||||
if (nodeData.getVersion() < CaseNodeData.getCurrentVersion()) {
|
||||
nodeData = updateNodeData(nodeName, nodeData);
|
||||
}
|
||||
} else {
|
||||
nodeData = createNodeDataFromCaseMetadata(nodeName, false);
|
||||
nodeData = updateNodeData(nodeName, null);
|
||||
}
|
||||
if (nodeData != null) {
|
||||
cases.add(nodeData);
|
||||
}
|
||||
cases.add(nodeData);
|
||||
|
||||
} catch (CoordinationService.CoordinationServiceException | InterruptedException | IOException | ParseException | CaseMetadata.CaseMetadataException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error getting coordination service node data for %s", nodeName), ex);
|
||||
@ -109,15 +93,15 @@ final public class MultiUserCaseNodeDataCollector {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and saves case directory lock coordination service node data from
|
||||
* the metadata file for the case associated with the node.
|
||||
* Updates the case directory lock coordination service node data for a
|
||||
* case.
|
||||
*
|
||||
* @param nodeName The coordination service node name, i.e., the case
|
||||
* directory path.
|
||||
* @param errorsOccurred Whether or not errors occurred during an auto
|
||||
* ingest job for the case.
|
||||
* @param nodeName The coordination service node name, i.e., the case
|
||||
* directory path.
|
||||
* @param oldNodeData .
|
||||
*
|
||||
* @return A CaseNodedata object.
|
||||
* @return A CaseNodedata object or null if the coordination service node is
|
||||
* an "orphan" with no corresponding case directry.
|
||||
*
|
||||
* @throws IOException If there is an error writing the
|
||||
* node data to a byte array.
|
||||
@ -130,28 +114,67 @@ final public class MultiUserCaseNodeDataCollector {
|
||||
* @throws InterruptedException If a coordination service operation
|
||||
* is interrupted.
|
||||
*/
|
||||
private static CaseNodeData createNodeDataFromCaseMetadata(String nodeName, boolean errorsOccurred) throws IOException, CaseMetadata.CaseMetadataException, ParseException, CoordinationService.CoordinationServiceException, InterruptedException {
|
||||
CaseNodeData nodeData = null;
|
||||
private static CaseNodeData updateNodeData(String nodeName, CaseNodeData oldNodeData) throws IOException, CaseMetadata.CaseMetadataException, ParseException, CoordinationService.CoordinationServiceException, InterruptedException {
|
||||
Path caseDirectoryPath = Paths.get(nodeName).toRealPath(LinkOption.NOFOLLOW_LINKS);
|
||||
File caseDirectory = caseDirectoryPath.toFile();
|
||||
if (caseDirectory.exists()) {
|
||||
if (!caseDirectory.exists()) {
|
||||
logger.log(Level.WARNING, String.format("Found orphan coordination service node %s, attempting clean up", caseDirectoryPath));
|
||||
deleteLockNodes(CoordinationService.getInstance(), caseDirectoryPath);
|
||||
return null;
|
||||
}
|
||||
|
||||
CaseNodeData nodeData = null;
|
||||
if (oldNodeData == null || oldNodeData.getVersion() == 0) {
|
||||
File[] files = caseDirectory.listFiles();
|
||||
for (File file : files) {
|
||||
String name = file.getName().toLowerCase();
|
||||
if (name.endsWith(CaseMetadata.getFileExtension())) {
|
||||
CaseMetadata metadata = new CaseMetadata(Paths.get(file.getAbsolutePath()));
|
||||
nodeData = new CaseNodeData(metadata);
|
||||
nodeData.setErrorsOccurred(errorsOccurred);
|
||||
if (oldNodeData != null) {
|
||||
/*
|
||||
* Version 0 case node data was only written if errors
|
||||
* occurred during an auto ingest job.
|
||||
*/
|
||||
nodeData.setErrorsOccurred(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeData != null) {
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodeName, nodeData.toArray());
|
||||
return nodeData;
|
||||
} else {
|
||||
throw new IOException(String.format("Could not find case metadata file for %s", nodeName));
|
||||
CoordinationService.getInstance().setNodeData(CoordinationService.CategoryNode.CASES, nodeName, nodeData.toArray());
|
||||
}
|
||||
|
||||
return nodeData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to delete the coordination service lock nodes for a case,
|
||||
* logging any failures.
|
||||
*
|
||||
* @param coordinationService The coordination service.
|
||||
* @param caseDirectoryPath The case directory path.
|
||||
*/
|
||||
private static void deleteLockNodes(CoordinationService coordinationService, Path caseDirectoryPath) {
|
||||
deleteCoordinationServiceNode(coordinationService, CaseCoordinationServiceUtils.getCaseResourcesLockName(caseDirectoryPath));
|
||||
deleteCoordinationServiceNode(coordinationService, CaseCoordinationServiceUtils.getCaseAutoIngestLogLockName(caseDirectoryPath));
|
||||
deleteCoordinationServiceNode(coordinationService, CaseCoordinationServiceUtils.getCaseDirectoryLockName(caseDirectoryPath));
|
||||
deleteCoordinationServiceNode(coordinationService, CaseCoordinationServiceUtils.getCaseLockName(caseDirectoryPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to delete a coordination service node, logging failure.
|
||||
*
|
||||
* @param coordinationService The coordination service.
|
||||
* @param nodeName A node name.
|
||||
*/
|
||||
private static void deleteCoordinationServiceNode(CoordinationService coordinationService, String nodeName) {
|
||||
try {
|
||||
coordinationService.deleteNode(CoordinationService.CategoryNode.CASES, nodeName);
|
||||
} catch (CoordinationService.CoordinationServiceException | InterruptedException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error deleting coordination service node %s", nodeName), ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
MultiUserCaseBrowserCustomizer.column.caseDbDeleteStatus=Case Database Deleted
|
||||
MultiUserCaseBrowserCustomizer.column.caseDirDeleteStatus=Case Directory Deleted
|
||||
MultiUserCaseBrowserCustomizer.column.createTime=Create Time
|
||||
MultiUserCaseBrowserCustomizer.column.dataSourcesDeleteStatus=Data Sources Deleted
|
||||
MultiUserCaseBrowserCustomizer.column.directory=Directory
|
||||
MultiUserCaseBrowserCustomizer.column.displayName=Name
|
||||
MultiUserCaseBrowserCustomizer.column.lastAccessTime=Last Access Time
|
||||
MultiUserCaseBrowserCustomizer.column.manifestCoordSvcNodesDeleteStatus=Manifest ZooKeeper Node Deleted
|
||||
MultiUserCaseBrowserCustomizer.column.textIndexDeleteStatus=Text Index Deleted
|
||||
MultiUserCasesBrowserPanel.waitNode.message=Please Wait...
|
||||
|
@ -141,14 +141,23 @@ public interface MultiUserCaseBrowserCustomizer {
|
||||
"MultiUserCaseBrowserCustomizer.column.displayName=Name",
|
||||
"MultiUserCaseBrowserCustomizer.column.createTime=Create Time",
|
||||
"MultiUserCaseBrowserCustomizer.column.directory=Directory",
|
||||
"MultiUserCaseBrowserCustomizer.column.lastAccessTime=Last Access Time"
|
||||
"MultiUserCaseBrowserCustomizer.column.lastAccessTime=Last Access Time",
|
||||
"MultiUserCaseBrowserCustomizer.column.textIndexDeleteStatus=Text Index Deleted",
|
||||
"MultiUserCaseBrowserCustomizer.column.caseDbDeleteStatus=Case Database Deleted",
|
||||
"MultiUserCaseBrowserCustomizer.column.caseDirDeleteStatus=Case Directory Deleted",
|
||||
"MultiUserCaseBrowserCustomizer.column.dataSourcesDeleteStatus=Data Sources Deleted",
|
||||
"MultiUserCaseBrowserCustomizer.column.manifestCoordSvcNodesDeleteStatus=Manifest ZooKeeper Node Deleted"
|
||||
})
|
||||
public enum Column {
|
||||
DISPLAY_NAME(Bundle.MultiUserCaseBrowserCustomizer_column_displayName()),
|
||||
CREATE_DATE(Bundle.MultiUserCaseBrowserCustomizer_column_createTime()),
|
||||
DIRECTORY(Bundle.MultiUserCaseBrowserCustomizer_column_directory()),
|
||||
LAST_ACCESS_DATE(Bundle.MultiUserCaseBrowserCustomizer_column_lastAccessTime());
|
||||
// RJCTODO: Add properties for deleted items flags
|
||||
LAST_ACCESS_DATE(Bundle.MultiUserCaseBrowserCustomizer_column_lastAccessTime()),
|
||||
TEXT_INDEX_DELETION_STATUS(Bundle.MultiUserCaseBrowserCustomizer_column_textIndexDeleteStatus()),
|
||||
CASE_DB_DELETION_STATUS(Bundle.MultiUserCaseBrowserCustomizer_column_caseDbDeleteStatus()),
|
||||
CASE_DIR_DELETION_STATUS(Bundle.MultiUserCaseBrowserCustomizer_column_caseDirDeleteStatus()),
|
||||
DATA_SOURCES_DELETION_STATUS(Bundle.MultiUserCaseBrowserCustomizer_column_dataSourcesDeleteStatus()),
|
||||
MANIFEST_FILE_LOCK_NODES_DELETION_STATUS(Bundle.MultiUserCaseBrowserCustomizer_column_manifestCoordSvcNodesDeleteStatus());
|
||||
|
||||
private final String displayName;
|
||||
|
||||
|
@ -65,7 +65,7 @@ final class MultiUserCasesRootNode extends AbstractNode {
|
||||
try {
|
||||
List<CaseNodeData> caseNodeData = MultiUserCaseNodeDataCollector.getNodeData();
|
||||
keys.addAll(caseNodeData);
|
||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
||||
} catch (CoordinationService.CoordinationServiceException | InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get case node data from coodination service", ex);
|
||||
}
|
||||
return true;
|
||||
|
@ -363,15 +363,22 @@ public final class CoordinationService {
|
||||
* @param category The desired category in the namespace.
|
||||
* @param nodePath The node to be deleted.
|
||||
*
|
||||
* @throws CoordinationServiceException If there is an error deleting the
|
||||
* node.
|
||||
* @throws CoordinationServiceException If there is an error deleting the
|
||||
* node.
|
||||
* @throws java.lang.InterruptedException If a thread interrupt occurs while
|
||||
* blocked waiting for the operation
|
||||
* to complete.
|
||||
*/
|
||||
public void deleteNode(CategoryNode category, String nodePath) throws CoordinationServiceException {
|
||||
public void deleteNode(CategoryNode category, String nodePath) throws CoordinationServiceException, InterruptedException {
|
||||
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
|
||||
try {
|
||||
curator.delete().forPath(fullNodePath);
|
||||
} catch (Exception ex) {
|
||||
throw new CoordinationServiceException(String.format("Failed to delete node %s", fullNodePath), ex);
|
||||
if (ex instanceof InterruptedException) {
|
||||
throw (InterruptedException) ex;
|
||||
} else {
|
||||
throw new CoordinationServiceException(String.format("Failed to delete node %s", fullNodePath), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,15 +389,22 @@ public final class CoordinationService {
|
||||
*
|
||||
* @return A list of child node names.
|
||||
*
|
||||
* @throws CoordinationServiceException If there is an error getting the
|
||||
* node list.
|
||||
* @throws CoordinationServiceException If there is an error getting the
|
||||
* node list.
|
||||
* @throws java.lang.InterruptedException If a thread interrupt occurs while
|
||||
* blocked waiting for the operation
|
||||
* to complete.
|
||||
*/
|
||||
public List<String> getNodeList(CategoryNode category) throws CoordinationServiceException {
|
||||
public List<String> getNodeList(CategoryNode category) throws CoordinationServiceException, InterruptedException {
|
||||
try {
|
||||
List<String> list = curator.getChildren().forPath(categoryNodeToPath.get(category.getDisplayName()));
|
||||
return list;
|
||||
} catch (Exception ex) {
|
||||
throw new CoordinationServiceException(String.format("Failed to get node list for %s", category.getDisplayName()), ex);
|
||||
if (ex instanceof InterruptedException) {
|
||||
throw (InterruptedException) ex;
|
||||
} else {
|
||||
throw new CoordinationServiceException(String.format("Failed to get node list for %s", category.getDisplayName()), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,9 +418,9 @@ public final class CoordinationService {
|
||||
*/
|
||||
private String getFullyQualifiedNodePath(CategoryNode category, String nodePath) {
|
||||
// nodePath on Unix systems starts with a "/" and ZooKeeper doesn't like two slashes in a row
|
||||
if(nodePath.startsWith("/")){
|
||||
if (nodePath.startsWith("/")) {
|
||||
return categoryNodeToPath.get(category.getDisplayName()) + nodePath.toUpperCase();
|
||||
}else{
|
||||
} else {
|
||||
return categoryNodeToPath.get(category.getDisplayName()) + "/" + nodePath.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
@ -2292,9 +2292,9 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
|
||||
sysLogger.log(Level.INFO, "Opening case {0} for {1}", new Object[]{caseName, manifest.getFilePath()});
|
||||
currentJob.setProcessingStage(AutoIngestJob.Stage.OPENING_CASE, Date.from(Instant.now()));
|
||||
/*
|
||||
* Acquire and hold a case name lock so that only one node at as
|
||||
* time can scan the output directory at a time. This prevents
|
||||
* making duplicate cases for the same auto ingest case.
|
||||
* Acquire and hold a case name lock so that only one node at a time
|
||||
* can search the output directory for an existing case. This
|
||||
* prevents making duplicate cases for the same auto ingest case.
|
||||
*/
|
||||
try (Lock caseLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, caseName, 30, TimeUnit.MINUTES)) {
|
||||
if (null != caseLock) {
|
||||
|
@ -96,7 +96,7 @@ final class AutoIngestMetricsCollector {
|
||||
|
||||
return newMetricsSnapshot;
|
||||
|
||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
||||
} catch (CoordinationService.CoordinationServiceException | InterruptedException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to get node list from coordination service", ex);
|
||||
return new MetricsSnapshot();
|
||||
}
|
||||
|
@ -378,7 +378,7 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
|
||||
|
||||
return newJobsSnapshot;
|
||||
|
||||
} catch (CoordinationServiceException ex) {
|
||||
} catch (CoordinationServiceException | InterruptedException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to get node list from coordination service", ex);
|
||||
return new JobsSnapshot();
|
||||
}
|
||||
|
@ -180,26 +180,31 @@ DeleteCaseInputAndOutputAction.taskName=input-and-output
|
||||
DeleteCaseOutputAction.menuItemText=Delete Output
|
||||
DeleteCaseOutputAction.progressDisplayName=Delete Output
|
||||
DeleteCaseOutputAction.taskName=output
|
||||
DeleteCaseTask.progress.acquiringCaseDirLock=Acquiring exclusive case directory lock
|
||||
DeleteCaseTask.progress.acquiringCaseNameLock=Acquiring exclusive case name lock
|
||||
DeleteCaseTask.progress.acquiringCaseDirLock=Acquiring an exclusive case directory lock...
|
||||
DeleteCaseTask.progress.acquiringCaseNameLock=Acquiring an exclusive case name lock...
|
||||
DeleteCaseTask.progress.acquiringInputDirLocks=Acquiring exclusive input directory locks
|
||||
DeleteCaseTask.progress.connectingToCoordSvc=Connecting to the coordination service
|
||||
DeleteCaseTask.progress.deletingCaseOutput=Deleting case output
|
||||
DeleteCaseTask.progress.deletingDirLockNode=Deleting case directory lock node
|
||||
DeleteCaseTask.progress.acquiringManifestFileLocks=Acquiring exclusive manifest file locks...
|
||||
DeleteCaseTask.progress.acquiringManifestLocks=Acquiring exclusive manifest file locks...
|
||||
DeleteCaseTask.progress.connectingToCoordSvc=Connecting to the coordination service...
|
||||
DeleteCaseTask.progress.deletingCaseOutput=Deleting case database, text index, and directory...
|
||||
DeleteCaseTask.progress.deletingDirLockNode=Deleting case directory lock coordination service node...
|
||||
# {0} - input directory name
|
||||
DeleteCaseTask.progress.deletingInputDir=Deleting input directory {0}
|
||||
DeleteCaseTask.progress.deletingInputDir=Deleting input directory {0}...
|
||||
DeleteCaseTask.progress.deletingInputDirLockNodes=Deleting input directory lock nodes
|
||||
DeleteCaseTask.progress.deletingInputDirs=Deleting input directory
|
||||
DeleteCaseTask.progress.deletingJobLogLockNode=Deleting auto ingest job log lock node
|
||||
DeleteCaseTask.progress.deletingNameLockNode=Deleting case name lock node
|
||||
DeleteCaseTask.progress.deletingInputDirs=Deleting input directory...
|
||||
DeleteCaseTask.progress.deletingJobLogLockNode=Deleting case auto ingest job log lock node...
|
||||
DeleteCaseTask.progress.deletingManifestFileLockNodes=Deleting manifest file lock nodes...
|
||||
DeleteCaseTask.progress.deletingNameLockNode=Deleting case name lock node...
|
||||
DeleteCaseTask.progress.deletingResourcesLockNode=Deleting case resources lock node
|
||||
DeleteCaseTask.progress.gettingJobNodeData=Getting node data for auto ingest jobs
|
||||
DeleteCaseTask.progress.locatingCaseMetadataFile=Locating case metadata file
|
||||
# {0} - input directory name
|
||||
DeleteCaseTask.progress.lockingInputDir=Acquiring exclusive lock on input directory {0}
|
||||
DeleteCaseTask.progress.gettingJobNodeData=Getting node data for auto ingest jobs...
|
||||
DeleteCaseTask.progress.locatingCaseMetadataFile=Locating case metadata file...
|
||||
DeleteCaseTask.progress.lockingInputDir=Acquiring exclusive lock on manifest {0}
|
||||
# {0} - manifest file name
|
||||
DeleteCaseTask.progress.lockingManifestFile=Acquiring exclusive lock on manifest {0}...
|
||||
# {0} - manifest file path
|
||||
DeleteCaseTask.progress.releasingManifestLock=Releasing the exclusive lock on manifest file {0}
|
||||
DeleteCaseTask.progress.releasingManifestLocks=Acquiring exclusive manifest file locks
|
||||
DeleteCaseTask.progress.releasingManifestLock=Releasing the exclusive lock on manifest file {0}...
|
||||
DeleteCaseTask.progress.releasingManifestLocks=Releasing exclusive manifest file locks...
|
||||
DeleteCaseTask.progress.startMessage=Preparing for deletion...
|
||||
GeneralFilter.archiveDesc.text=Archive Files (.zip, .rar, .arj, .7z, .7zip, .gzip, .gz, .bzip2, .tar, .tgz)
|
||||
HINT_CasesDashboardTopComponent=This is an adminstrative dashboard for multi-user cases
|
||||
OpenAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted.
|
||||
|
@ -38,19 +38,24 @@ final class DeleteCaseInputAndOutputTask extends DeleteCaseTask {
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteWhileHoldingAllLocks() {
|
||||
void deleteWhileHoldingAllLocks() throws InterruptedException {
|
||||
deleteInputDirectories();
|
||||
deleteCaseOutput();
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteAfterCaseLocksReleased() {
|
||||
deleteCaseLockNodes();
|
||||
void deleteAfterManifestLocksReleased() throws InterruptedException {
|
||||
deleteManifestFileLockNodes();
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteAfterCaseDirectoryLockReleased() throws InterruptedException {
|
||||
this.deleteCaseDirectoryLockNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteAfterAllLocksReleased() {
|
||||
deleteInputDirectoryLockNodes();
|
||||
void deleteAfterCaseNameLockReleased() throws InterruptedException {
|
||||
this.deleteCaseNameLockNode();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,18 +24,18 @@ import org.sleuthkit.autopsy.progress.ProgressIndicator;
|
||||
/**
|
||||
* A task to delete the auto ingest job input directories for a case produced
|
||||
* via auto ingest, while leaving the auto ingest job coordination service nodes
|
||||
* and the rest of the case intact. The use case is freeing space while
|
||||
* retaining the option to restore the input directories, effectively restoring
|
||||
* the case.
|
||||
* (manifest file lock nodes) and the rest of the case intact. The use case is
|
||||
* freeing space while retaining the option to restore the input directories,
|
||||
* effectively restoring the case.
|
||||
*/
|
||||
final class DeleteCaseInputTask extends DeleteCaseTask {
|
||||
|
||||
/**
|
||||
* Constructs a task to delete the auto ingest job input directories for a
|
||||
* Constructs task to delete the auto ingest job input directories for a
|
||||
* case produced via auto ingest, while leaving the auto ingest job
|
||||
* coordination service nodes and the rest of the case intact. The use case
|
||||
* is freeing space while retaining the option to restore the input
|
||||
* directories, effectively restoring the case.
|
||||
* coordination service nodes (manifest file lock nodes) and the rest of the
|
||||
* case intact. The use case is freeing space while retaining the option to
|
||||
* restore the input directories, effectively restoring the case.
|
||||
*
|
||||
* @param caseNodeData The case directory lock coordination service node
|
||||
* data for the case.
|
||||
@ -46,16 +46,20 @@ final class DeleteCaseInputTask extends DeleteCaseTask {
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteWhileHoldingAllLocks() {
|
||||
void deleteWhileHoldingAllLocks() throws InterruptedException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteAfterCaseDirectoryLockReleased() throws InterruptedException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteAfterCaseNameLockReleased() throws InterruptedException {
|
||||
deleteInputDirectories();
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteAfterCaseLocksReleased() {
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteAfterAllLocksReleased() {
|
||||
void deleteAfterManifestLocksReleased() throws InterruptedException {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,12 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.Utilities;
|
||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
|
||||
import org.sleuthkit.autopsy.progress.ProgressIndicator;
|
||||
|
||||
|
@ -45,17 +45,22 @@ final class DeleteCaseOutputTask extends DeleteCaseTask {
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteWhileHoldingAllLocks() {
|
||||
void deleteWhileHoldingAllLocks() throws InterruptedException {
|
||||
deleteCaseOutput();
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteAfterCaseLocksReleased() {
|
||||
void deleteAfterCaseDirectoryLockReleased() throws InterruptedException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteAfterAllLocksReleased() {
|
||||
deleteInputDirectoryLockNodes();
|
||||
void deleteAfterManifestLocksReleased() throws InterruptedException {
|
||||
deleteManifestFileLockNodes();
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteAfterCaseNameLockReleased() throws InterruptedException {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
@ -29,138 +30,74 @@ import java.util.logging.Level;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseActionException;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
|
||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseCoordinationServiceUtils;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||
import org.sleuthkit.autopsy.progress.ProgressIndicator;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
|
||||
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobNodeData.InvalidDataException;
|
||||
|
||||
/**
|
||||
* A base class for tasks that delete part or all of a case produced via auto
|
||||
* ingest.
|
||||
* A base class for tasks that delete part or all of a given case.
|
||||
*/
|
||||
abstract class DeleteCaseTask implements Runnable {
|
||||
|
||||
private static final String RESOURCES_LOCK_SUFFIX = "_resources"; //NON-NLS
|
||||
private static final Logger logger = AutoIngestDashboardLogger.getLogger();
|
||||
private final CaseNodeData caseNodeData;
|
||||
private final String caseDisplayName;
|
||||
private final String caseUniqueName;
|
||||
private final Path caseDirectoryPath;
|
||||
private final ProgressIndicator progress;
|
||||
private final List<AutoIngestJobNodeData> nodeDataForAutoIngestJobs;
|
||||
private final Map<Path, CoordinationService.Lock> manifestFileLocks;
|
||||
private final Map<String, CoordinationService.Lock> manifestFileLocks;
|
||||
private CoordinationService coordinationService;
|
||||
|
||||
/**
|
||||
* Constructs the base class part of a task that deletes part or all of a
|
||||
* case produced via auto ingest.
|
||||
* given case.
|
||||
*
|
||||
* @param caseNodeData The case directory lock coordination service node
|
||||
* data for the case to be deleted.
|
||||
* data for the case.
|
||||
* @param progress A progress indicator.
|
||||
*/
|
||||
DeleteCaseTask(CaseNodeData caseNodeData, ProgressIndicator progress) {
|
||||
this.caseNodeData = caseNodeData;
|
||||
this.progress = progress;
|
||||
/*
|
||||
* Design Decision Note: It was decided to add the following state to
|
||||
* instances of this class make it easier to access given that the class
|
||||
* design favors instance methods over static methods.
|
||||
*/
|
||||
this.caseDisplayName = caseNodeData.getDisplayName();
|
||||
this.caseUniqueName = caseNodeData.getName();
|
||||
this.caseDirectoryPath = caseNodeData.getDirectory();
|
||||
this.nodeDataForAutoIngestJobs = new ArrayList<>();
|
||||
this.manifestFileLocks = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({
|
||||
"DeleteCaseTask.progress.connectingToCoordSvc=Connecting to the coordination service",
|
||||
"DeleteCaseTask.progress.acquiringCaseNameLock=Acquiring exclusive case name lock",
|
||||
"DeleteCaseTask.progress.acquiringCaseDirLock=Acquiring exclusive case directory lock",
|
||||
"DeleteCaseTask.progress.gettingJobNodeData=Getting node data for auto ingest jobs",
|
||||
"DeleteCaseTask.progress.acquiringInputDirLocks=Acquiring exclusive input directory locks"
|
||||
"DeleteCaseTask.progress.startMessage=Preparing for deletion..."
|
||||
})
|
||||
public void run() {
|
||||
try {
|
||||
progress.start(Bundle.DeleteCaseTask_progress_connectingToCoordSvc());
|
||||
try {
|
||||
coordinationService = CoordinationService.getInstance();
|
||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to connect to the coordination service to delete %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Acquire an exclusive case name lock. This is the lock that auto
|
||||
* ingest nodes acquire when creating or opening a case specified in
|
||||
* an auto ingest job manifest file. Acquiring this lock prevents
|
||||
* auto ingest nodes from searching for and finding the case
|
||||
* directory of the case to be deleted.
|
||||
*/
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_acquiringCaseNameLock());
|
||||
logger.log(Level.INFO, String.format("Exclusively locking the case name for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
final String caseNameLockName = TimeStampUtils.removeTimeStamp(caseNodeData.getName());
|
||||
try (CoordinationService.Lock nameLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseNameLockName)) {
|
||||
if (nameLock == null) {
|
||||
logger.log(Level.WARNING, String.format("Failed to exclusively lock the case name for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Acquire an exclusive case directory lock. This is the lock
|
||||
* that is aquired by any node (auto ingest or examiner)
|
||||
* attempting to create or open a case and is held by such a
|
||||
* node for as long as the case is open. Acquiring this lock
|
||||
* ensures that no other node currently has the case to be
|
||||
* deleted open and prevents another node from trying to open
|
||||
* the case as it is being deleted.
|
||||
*/
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_acquiringCaseDirLock());
|
||||
logger.log(Level.INFO, String.format("Exclusively locking the case directory for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
try (CoordinationService.Lock caseLock = CoordinationService.getInstance().tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, caseNodeData.getDirectory().toString())) {
|
||||
if (caseLock == null) {
|
||||
logger.log(Level.WARNING, String.format("Failed to exclusively lock the case directory for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
return;
|
||||
}
|
||||
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_gettingJobNodeData());
|
||||
logger.log(Level.INFO, String.format("Fetching auto ingest job node data for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
try {
|
||||
getAutoIngestJobNodeData();
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to fetch auto ingest job node data for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nodeDataForAutoIngestJobs.isEmpty()) {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_acquiringInputDirLocks());
|
||||
logger.log(Level.INFO, String.format("Exclusively locking the case directories for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
getInputDirectoryLocks();
|
||||
if (manifestFileLocks.isEmpty()) {
|
||||
logger.log(Level.WARNING, String.format("Failed to exclusively lock the input directories for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logger.log(Level.INFO, String.format("No auto ingest job node data found for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
}
|
||||
|
||||
deleteWhileHoldingAllLocks();
|
||||
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error acquiring exclusive case directory lock for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
}
|
||||
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error acquiring exclusive case name lock for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
}
|
||||
|
||||
deleteAfterCaseLocksReleased();
|
||||
releaseInputDirectoryLocks();
|
||||
deleteAfterAllLocksReleased();
|
||||
progress.start(Bundle.DeleteCaseTask_progress_startMessage());
|
||||
logger.log(Level.INFO, String.format("Beginning deletion of %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
deleteCase();
|
||||
logger.log(Level.SEVERE, String.format("Deletion of %s (%s) in %s completed", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
|
||||
} catch (Throwable ex) {
|
||||
/*
|
||||
* Unexpected runtime exceptions firewall.
|
||||
* Unexpected runtime exceptions firewall. This task is designed to
|
||||
* be able to be run in an executor service thread pool without
|
||||
* calling get() on the task's Future<Void>, so this ensures that
|
||||
* such errors do get ignored.
|
||||
*/
|
||||
logger.log(Level.SEVERE, String.format("Unexpected error deleting %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
logger.log(Level.INFO, String.format("Unexpected error deleting %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
|
||||
} finally {
|
||||
progress.finish();
|
||||
@ -169,224 +106,392 @@ abstract class DeleteCaseTask implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the parts of the case that need to be deleted while holding the
|
||||
* following exclusive locks: case name lock, case directory lock, and all
|
||||
* input directory locks.
|
||||
*/
|
||||
abstract void deleteWhileHoldingAllLocks();
|
||||
|
||||
/**
|
||||
* Deletes the parts of the case that need to be deleted after releasing
|
||||
* exclusive locks on the case name and case directory, but while still
|
||||
* holding exclusive locks on all of the input directories.
|
||||
*/
|
||||
abstract void deleteAfterCaseLocksReleased();
|
||||
|
||||
/**
|
||||
* Deletes the parts of the case that need to be deleted after all of the
|
||||
* locks are released.
|
||||
*/
|
||||
abstract void deleteAfterAllLocksReleased();
|
||||
|
||||
/**
|
||||
* Deletes the auto ingest job input directories for the case. Should only
|
||||
* be called when holding all of the exclusive locks for the case.
|
||||
* Deletes part or all of the given case.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"DeleteCaseTask.progress.deletingInputDirs=Deleting input directory",
|
||||
"# {0} - input directory name", "DeleteCaseTask.progress.deletingInputDir=Deleting input directory {0}"
|
||||
"DeleteCaseTask.progress.connectingToCoordSvc=Connecting to the coordination service...",
|
||||
"DeleteCaseTask.progress.acquiringCaseNameLock=Acquiring an exclusive case name lock...",
|
||||
"DeleteCaseTask.progress.acquiringCaseDirLock=Acquiring an exclusive case directory lock...",
|
||||
"DeleteCaseTask.progress.gettingJobNodeData=Getting node data for auto ingest jobs...",
|
||||
"DeleteCaseTask.progress.acquiringManifestLocks=Acquiring exclusive manifest file locks..."
|
||||
})
|
||||
protected void deleteInputDirectories() {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingInputDirs());
|
||||
logger.log(Level.INFO, String.format("Deleting input directories for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
for (AutoIngestJobNodeData jobNodeData : nodeDataForAutoIngestJobs) {
|
||||
final Path inputDirPath = jobNodeData.getManifestFilePath().getParent();
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingInputDir(inputDirPath));
|
||||
logger.log(Level.INFO, String.format("Deleting input directory %s for %s (%s) in %s", inputDirPath, caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
if (FileUtil.deleteDir(new File(inputDirPath.toString()))) {
|
||||
logger.log(Level.WARNING, String.format("Failed to delete the input directory %s for %s (%s) in %s", inputDirPath, caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
// RJCTODO: Update deletion flags
|
||||
private void deleteCase() {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_connectingToCoordSvc());
|
||||
logger.log(Level.INFO, String.format("Connecting to coordination service for deletion of %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
try {
|
||||
coordinationService = CoordinationService.getInstance();
|
||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to connect to the coordination service, cannot delete %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Acquire an exclusive case name lock. This is the lock that auto
|
||||
* ingest nodes acquire exclusively when creating or opening a case
|
||||
* specified in an auto ingest job manifest file to ensure that only one
|
||||
* auto ingest node at a time can search the auto ingest output
|
||||
* directory for an existing case matching the one in the manifest file.
|
||||
* Acquiring this lock effectively locks auto ingest node job processing
|
||||
* tasks out of the case to be deleted.
|
||||
*/
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_acquiringCaseNameLock());
|
||||
logger.log(Level.INFO, String.format("Acquiring an exclusive case name lock for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
String caseNameLockNodeName = CaseCoordinationServiceUtils.getCaseLockName(caseDirectoryPath);
|
||||
try (CoordinationService.Lock nameLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseNameLockNodeName)) {
|
||||
if (nameLock == null) {
|
||||
logger.log(Level.INFO, String.format("Could not delete %s (%s) in %s because a case name lock was held by another host", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Acquire an exclusive case directory lock. A shared case directory
|
||||
* lock is acquired by any node (auto ingest or examiner) when it
|
||||
* opens a case and is held by the node for as long as the case is
|
||||
* open. Acquiring this lock exclusively ensures that no other node
|
||||
* currently has the case to be deleted open and prevents another
|
||||
* node from trying to open the case while it is being deleted.
|
||||
*/
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_acquiringCaseDirLock());
|
||||
logger.log(Level.INFO, String.format("Acquiring an exclusive case directory lock for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
String caseDirLockNodeName = CaseCoordinationServiceUtils.getCaseDirectoryLockName(caseDirectoryPath);
|
||||
try (CoordinationService.Lock caseDirLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, caseDirLockNodeName)) {
|
||||
if (caseDirLock == null) {
|
||||
logger.log(Level.INFO, String.format("Could not delete %s (%s) in %s because a case directory lock was held by another host", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
return;
|
||||
}
|
||||
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_gettingJobNodeData());
|
||||
logger.log(Level.INFO, String.format("Fetching auto ingest job node data for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
try {
|
||||
getAutoIngestJobNodeData();
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error fetching auto ingest job node data for %s (%s) in %s, cannot delete case", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
return;
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nodeDataForAutoIngestJobs.isEmpty()) {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_acquiringManifestLocks());
|
||||
logger.log(Level.INFO, String.format("Acquiring exclusive manifest file locks for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
getManifestFileLocks();
|
||||
if (manifestFileLocks.isEmpty()) {
|
||||
logger.log(Level.INFO, String.format("Could not delete %s (%s) in %s because a case directory lock was held by another host", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logger.log(Level.INFO, String.format("No auto ingest job node data found for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
releaseManifestFileLocks();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
deleteWhileHoldingAllLocks();
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
return;
|
||||
}
|
||||
|
||||
releaseManifestFileLocks();
|
||||
|
||||
try {
|
||||
deleteAfterManifestLocksReleased();
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error acquiring exclusive case directory lock for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
deleteAfterCaseDirectoryLockReleased();
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error acquiring exclusive case name lock for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
}
|
||||
|
||||
try {
|
||||
deleteAfterCaseNameLockReleased();
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the case directory, the case database, and the text index for the
|
||||
* case. Should only be called when holding all of the exclusive locks for
|
||||
* the case.
|
||||
* Deletes the parts of the case that need to be deleted while holding all
|
||||
* of the exclusive locks: the case name lock, the case directory lock, amd
|
||||
* the manifest file locks. Note that the locks are acquired in that order
|
||||
* and released in the opposite order.
|
||||
*/
|
||||
abstract void deleteWhileHoldingAllLocks() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Deletes the parts of the case that need to be deleted after the release
|
||||
* of the exclusive manifest file locks, while still holding the exclusive
|
||||
* case name and case directory locks; the manifest file locks are the first
|
||||
* locks released.
|
||||
*/
|
||||
abstract void deleteAfterManifestLocksReleased() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Deletes the parts of the case that need to be deleted after the release
|
||||
* of the exclusive manifest file locks and case directory lock, while still
|
||||
* holding the exclusive case name; the case name lock is the last lock
|
||||
* released.
|
||||
*/
|
||||
abstract void deleteAfterCaseDirectoryLockReleased() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Deletes the parts of the case that need to be deleted after the release
|
||||
* of all of the exclusive locks; the case name lock is the last lock
|
||||
* released.
|
||||
*/
|
||||
abstract void deleteAfterCaseNameLockReleased() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Deletes the auto ingest job input directories for the case. Intended to
|
||||
* be called by subclasses, if required, in their customization of the
|
||||
* deleteWhileHoldingAllLocks step of the case deletion algorithm.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"DeleteCaseTask.progress.locatingCaseMetadataFile=Locating case metadata file",
|
||||
"DeleteCaseTask.progress.deletingCaseOutput=Deleting case output"
|
||||
"# {0} - input directory name", "DeleteCaseTask.progress.deletingInputDir=Deleting input directory {0}..."
|
||||
})
|
||||
protected void deleteInputDirectories() {
|
||||
boolean allInputDirsDeleted = true;
|
||||
for (AutoIngestJobNodeData jobNodeData : nodeDataForAutoIngestJobs) {
|
||||
Path inputDirPath = jobNodeData.getManifestFilePath().getParent();
|
||||
File inputDir = inputDirPath.toFile();
|
||||
if (inputDir.exists()) {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingInputDir(inputDirPath));
|
||||
logger.log(Level.INFO, String.format("Deleting input directory %s for %s (%s) in %s", inputDirPath, caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
if (!FileUtil.deleteDir(new File(inputDirPath.toString()))) {
|
||||
logger.log(Level.WARNING, String.format("Failed to delete the input directory %s for %s (%s) in %s", inputDirPath, caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
allInputDirsDeleted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allInputDirsDeleted) {
|
||||
setDeletedItemFlag(CaseNodeData.DeletedFlags.DATA_SOURCES);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the case database, the text index, and the case directory for the
|
||||
* case. Intended to be called by subclasses, if required, in their
|
||||
* customization of the deleteWhileHoldingAllLocks step of the case deletion
|
||||
* algorithm.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"DeleteCaseTask.progress.locatingCaseMetadataFile=Locating case metadata file...",
|
||||
"DeleteCaseTask.progress.deletingCaseOutput=Deleting case database, text index, and directory...",
|
||||
"DeleteCaseTask.progress.deletingJobLogLockNode=Deleting case auto ingest job log lock node..."
|
||||
})
|
||||
protected void deleteCaseOutput() {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_locatingCaseMetadataFile());
|
||||
logger.log(Level.INFO, String.format("Locating metadata file for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
String metadataFilePath = null;
|
||||
final File caseDirectory = caseNodeData.getDirectory().toFile();
|
||||
logger.log(Level.INFO, String.format("Locating metadata file for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
CaseMetadata caseMetadata = null;
|
||||
final File caseDirectory = caseDirectoryPath.toFile();
|
||||
final File[] filesInDirectory = caseDirectory.listFiles();
|
||||
if (filesInDirectory != null) {
|
||||
for (File file : filesInDirectory) {
|
||||
if (file.getName().toLowerCase().endsWith(CaseMetadata.getFileExtension()) && file.isFile()) {
|
||||
metadataFilePath = file.getPath();
|
||||
try {
|
||||
caseMetadata = new CaseMetadata(Paths.get(file.getPath()));
|
||||
} catch (CaseMetadata.CaseMetadataException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error getting opening case metadata file for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (metadataFilePath != null) {
|
||||
if (caseMetadata != null) {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingCaseOutput());
|
||||
logger.log(Level.INFO, String.format("Deleting output for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
try {
|
||||
Case.deleteCase(new CaseMetadata(Paths.get(metadataFilePath)), false, progress);
|
||||
} catch (CaseMetadata.CaseMetadataException | CaseActionException ex) {
|
||||
// RJCTODO: Set delete flags?
|
||||
logger.log(Level.WARNING, String.format("Error deleting output for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
}
|
||||
logger.log(Level.INFO, String.format("Deleting output for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
Case.deleteMultiUserCase(caseNodeData, caseMetadata, progress); // RJCTODO: Make this method throw the interrupted exception.
|
||||
} else {
|
||||
logger.log(Level.WARNING, String.format("Failed to locate metadata file for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
// RJCTODO: Set delete flags
|
||||
logger.log(Level.WARNING, String.format("Failed to locate metadata file for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the case name, case directory, case resources, and case auto
|
||||
* ingest log for the case. Should only be called when holding the exclusive
|
||||
* locks for all of the input directories for the case.
|
||||
*/
|
||||
@Messages({
|
||||
"DeleteCaseTask.progress.deletingJobLogLockNode=Deleting auto ingest job log lock node",
|
||||
"DeleteCaseTask.progress.deletingResourcesLockNode=Deleting case resources lock node",
|
||||
"DeleteCaseTask.progress.deletingDirLockNode=Deleting case directory lock node",
|
||||
"DeleteCaseTask.progress.deletingNameLockNode=Deleting case name lock node"
|
||||
})
|
||||
protected void deleteCaseLockNodes() {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingJobLogLockNode());
|
||||
logger.log(Level.INFO, String.format("Deleting case auto ingest job log lock node for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
Path logFilePath = AutoIngestJobLogger.getLogPath(caseNodeData.getDirectory());
|
||||
logger.log(Level.INFO, String.format("Deleting case auto ingest job log lock node for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
Path logFilePath = AutoIngestJobLogger.getLogPath(caseDirectoryPath); //RJCTODO: USe util here
|
||||
try {
|
||||
coordinationService.deleteNode(CategoryNode.CASES, logFilePath.toString());
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error deleting auto ingest job log lock node for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
// RJCTODO: Set delete flags
|
||||
}
|
||||
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingResourcesLockNode());
|
||||
logger.log(Level.INFO, String.format("Deleting case resources log lock node for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
String resourcesLockNodePath = caseNodeData.getDirectory().toString() + RESOURCES_LOCK_SUFFIX;
|
||||
try {
|
||||
coordinationService.deleteNode(CategoryNode.CASES, resourcesLockNodePath);
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error deleting case resources lock node for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
// RJCTODO: Set delete flags
|
||||
}
|
||||
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingDirLockNode());
|
||||
logger.log(Level.INFO, String.format("Deleting case directory lock node for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
final Path caseDirectoryPath = caseNodeData.getDirectory();
|
||||
try {
|
||||
coordinationService.deleteNode(CategoryNode.CASES, caseDirectoryPath.toString());
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error deleting case directory lock node for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
// RJCTODO: Set delete flags
|
||||
}
|
||||
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingNameLockNode());
|
||||
logger.log(Level.INFO, String.format("Deleting case name lock node for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
final String caseNameLockName = TimeStampUtils.removeTimeStamp(caseNodeData.getName());
|
||||
try {
|
||||
coordinationService.deleteNode(CategoryNode.CASES, caseNameLockName);
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error deleting case name lock node for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
// RJCTODO: Set delete flags
|
||||
logger.log(Level.WARNING, String.format("Error deleting case auto ingest job log lock node for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the input directory lock nodes for the case. Should only be
|
||||
* called after releasing all of the locks for the case.
|
||||
* Deletes the manifest file lock coordination service nodes for the case.
|
||||
* Intended to be called by subclasses, if required, in their customization
|
||||
* of the deleteAfterManifestLocksReleased step of the case deletion
|
||||
* algorithm.
|
||||
*/
|
||||
@Messages({
|
||||
"DeleteCaseTask.progress.deletingInputDirLockNodes=Deleting input directory lock nodes"
|
||||
"DeleteCaseTask.progress.deletingManifestFileLockNodes=Deleting manifest file lock nodes..."
|
||||
})
|
||||
protected void deleteInputDirectoryLockNodes() {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingInputDirLockNodes());
|
||||
logger.log(Level.INFO, String.format("Deleting input directory lock nodes for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
protected void deleteManifestFileLockNodes() throws InterruptedException {
|
||||
boolean allInputDirsDeleted = true;
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingManifestFileLockNodes());
|
||||
logger.log(Level.INFO, String.format("Deleting manifest file lock nodes for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
for (AutoIngestJobNodeData jobNodeData : nodeDataForAutoIngestJobs) {
|
||||
try {
|
||||
logger.log(Level.INFO, String.format("Deleting manifest file lock node for %s for %s (%s) in %s", jobNodeData.getManifestFilePath(), caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
logger.log(Level.INFO, String.format("Deleting manifest file lock node for %s for %s (%s) in %s", jobNodeData.getManifestFilePath(), caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
coordinationService.deleteNode(CoordinationService.CategoryNode.MANIFESTS, jobNodeData.getManifestFilePath().toString());
|
||||
} catch (CoordinationServiceException ex) {
|
||||
Path inputDirPath = jobNodeData.getManifestFilePath().getParent();
|
||||
logger.log(Level.WARNING, String.format("Error deleting input directory lock node %s for %s (%s) in %s", inputDirPath, caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
// RJCTODO: Set delete flags
|
||||
logger.log(Level.WARNING, String.format("Error deleting manifest file lock node %s for %s (%s) in %s", jobNodeData.getManifestFilePath(), caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
allInputDirsDeleted = false;
|
||||
}
|
||||
}
|
||||
if (allInputDirsDeleted) {
|
||||
setDeletedItemFlag(CaseNodeData.DeletedFlags.MANIFEST_FILE_LOCK_NODES);
|
||||
}
|
||||
// RJCTODO: Expand case type in case metadata to include auto ingest cases.
|
||||
// Disable delete menu item for auto ingest cases, and possibly also add data source
|
||||
// capability.
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the case directory coordination service lock node for the case.
|
||||
* Intended to be called by subclasses, if required, in their customization
|
||||
* of the deleteAfterCaseDirectoryLockReleased step of the case deletion
|
||||
* algorithm.
|
||||
*/
|
||||
@Messages({
|
||||
"DeleteCaseTask.progress.deletingDirLockNode=Deleting case directory lock coordination service node..."
|
||||
})
|
||||
protected void deleteCaseDirectoryLockNode() throws InterruptedException {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingDirLockNode());
|
||||
try {
|
||||
Case.deleteCaseDirectoryLockNode(caseNodeData, progress); // RJCTODO: Case does not need to expose this?
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error deleting case directory lock node for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all of the auto ingest job (manifest file) lock coordination
|
||||
* service node data for a case.
|
||||
* Deletes the case name coordination service lock node for the case.
|
||||
* Intended to be called by subclasses, if required, in their customization
|
||||
* of the deleteAfterCaseNameLockReleased step of the case deletion
|
||||
* algorithm.
|
||||
*
|
||||
* @param caseName The name of the case.
|
||||
*
|
||||
* @return A list of auto ingest job (manifest file) lock node data for the
|
||||
* case.
|
||||
*
|
||||
* @throws CoordinationServiceException If there is an error getting a list
|
||||
* of the auto ingest job (manifest
|
||||
* file) lock nodes from the
|
||||
* coordination service.
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private void getAutoIngestJobNodeData() throws CoordinationServiceException {
|
||||
String caseName = caseNodeData.getName();
|
||||
final List<String> nodes = coordinationService.getNodeList(CoordinationService.CategoryNode.MANIFESTS);
|
||||
for (String nodeName : nodes) {
|
||||
@Messages({
|
||||
"DeleteCaseTask.progress.deletingNameLockNode=Deleting case name lock node..." // RJCTODO: Use consistent terminology
|
||||
})
|
||||
protected void deleteCaseNameLockNode() throws InterruptedException {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingNameLockNode());
|
||||
try {
|
||||
String caseNameLockNodeName = CaseCoordinationServiceUtils.getCaseLockName(caseDirectoryPath);
|
||||
coordinationService.deleteNode(CategoryNode.CASES, caseNameLockNodeName);
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error deleting case name lock node for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the auto ingest job data from the manifest file lock coordination
|
||||
* service nodes for a case.
|
||||
*
|
||||
* @throws CoordinationServiceException If there is an error interacting
|
||||
* with the coordination service.
|
||||
* @throws InterruptedException If the current thread is interrupted
|
||||
* while waiting for the coordination
|
||||
* service.
|
||||
*/
|
||||
private void getAutoIngestJobNodeData() throws CoordinationServiceException, InterruptedException {
|
||||
String caseName = caseDisplayName;
|
||||
final List<String> nodeNames = coordinationService.getNodeList(CoordinationService.CategoryNode.MANIFESTS);
|
||||
for (String nodeName : nodeNames) {
|
||||
try {
|
||||
byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodeName);
|
||||
if (nodeBytes == null || nodeBytes.length <= 0) {
|
||||
logger.log(Level.WARNING, String.format("Missing auto ingest job node data for manifest file lock node %s, deleting node", nodeName));
|
||||
try {
|
||||
coordinationService.deleteNode(CategoryNode.MANIFESTS, nodeName);
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.WARNING, String.format("Failed to delete empty manifest file lock node %s", nodeName));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(nodeBytes);
|
||||
if (caseName.equals(nodeData.getCaseName())) {
|
||||
nodeDataForAutoIngestJobs.add(nodeData);
|
||||
}
|
||||
} catch (CoordinationService.CoordinationServiceException | InterruptedException | InvalidDataException ex) {
|
||||
logger.log(Level.WARNING, String.format("Failed to get coordination service node data for %s", nodeName), ex);
|
||||
} catch (CoordinationService.CoordinationServiceException | InvalidDataException ex) {
|
||||
logger.log(Level.WARNING, String.format("Failed to get auto ingest job node data for %s", nodeName), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires either all or none of the input directory locks for a case.
|
||||
*
|
||||
* @param caseNodeData The case node data from the case
|
||||
* directory lock node for the case.
|
||||
* @param autoIngestJobNodeDataList The auto ingest job node data from the
|
||||
* input directory lock nodes for the case.
|
||||
*
|
||||
* @return A mapping of manifest file paths to input directory lcoks for all
|
||||
* input directories for the case; will be empty if all of the locks
|
||||
* could not be obtained.
|
||||
* Acquires either all or none of the manifest file locks for a case.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"# {0} - input directory name", "DeleteCaseTask.progress.lockingInputDir=Acquiring exclusive lock on input directory {0}",})
|
||||
private void getInputDirectoryLocks() {
|
||||
"# {0} - manifest file name", "DeleteCaseTask.progress.lockingManifestFile=Acquiring exclusive lock on manifest {0}..."
|
||||
})
|
||||
private void getManifestFileLocks() {
|
||||
for (AutoIngestJobNodeData autoIngestJobNodeData : nodeDataForAutoIngestJobs) {
|
||||
final Path inputDirPath = autoIngestJobNodeData.getManifestFilePath().getParent();
|
||||
String manifestPath = autoIngestJobNodeData.getManifestFilePath().toString();
|
||||
try {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_lockingInputDir(inputDirPath));
|
||||
final CoordinationService.Lock inputDirLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.MANIFESTS, autoIngestJobNodeData.getManifestFilePath().toString());
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_lockingManifestFile(manifestPath));
|
||||
logger.log(Level.INFO, String.format("Exclusively locking the manifest %s for %s (%s) in %s", manifestPath, caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
CoordinationService.Lock inputDirLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.MANIFESTS, manifestPath);
|
||||
if (null != inputDirLock) {
|
||||
manifestFileLocks.put(autoIngestJobNodeData.getManifestFilePath(), inputDirLock);
|
||||
manifestFileLocks.put(manifestPath, inputDirLock);
|
||||
} else {
|
||||
logger.log(Level.WARNING, String.format("Failed to exclusively lock the input directory %s for %s (%s) in %s", inputDirPath, caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
||||
releaseInputDirectoryLocks();
|
||||
logger.log(Level.INFO, String.format("Failed to exclusively lock the manifest %s for %s (%s) in %s", manifestPath, caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
releaseManifestFileLocks();
|
||||
manifestFileLocks.clear();
|
||||
break;
|
||||
}
|
||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to exclusively lock the input directory %s for %s (%s) in %s", inputDirPath, caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
releaseInputDirectoryLocks();
|
||||
logger.log(Level.SEVERE, String.format("Error exclusively locking the manifest %s for %s (%s) in %s", manifestPath, caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
releaseManifestFileLocks();
|
||||
manifestFileLocks.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -396,23 +501,36 @@ abstract class DeleteCaseTask implements Runnable {
|
||||
* for the case.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"DeleteCaseTask.progress.releasingManifestLocks=Acquiring exclusive manifest file locks",
|
||||
"# {0} - manifest file path", "DeleteCaseTask.progress.releasingManifestLock=Releasing the exclusive lock on manifest file {0}"
|
||||
"# {0} - manifest file path", "DeleteCaseTask.progress.releasingManifestLock=Releasing the exclusive lock on manifest file {0}..."
|
||||
})
|
||||
private void releaseInputDirectoryLocks() {
|
||||
private void releaseManifestFileLocks() {
|
||||
if (!manifestFileLocks.isEmpty()) {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_releasingManifestLocks());
|
||||
for (Map.Entry<Path, CoordinationService.Lock> entry : manifestFileLocks.entrySet()) {
|
||||
final Path manifestFilePath = entry.getKey();
|
||||
final CoordinationService.Lock manifestFileLock = entry.getValue();
|
||||
for (Map.Entry<String, CoordinationService.Lock> entry : manifestFileLocks.entrySet()) {
|
||||
String manifestFilePath = entry.getKey();
|
||||
CoordinationService.Lock manifestFileLock = entry.getValue();
|
||||
try {
|
||||
progress.progress(Bundle.DeleteCaseTask_progress_releasingManifestLock(manifestFilePath));
|
||||
logger.log(Level.INFO, String.format("Releasing the exclusive lock on the manifest file %s for %s (%s) in %s", manifestFilePath, caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||
manifestFileLock.release();
|
||||
} catch (CoordinationServiceException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error re3leasing exclusive lock on %s for %s (%s) in %s", manifestFilePath, caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
logger.log(Level.SEVERE, String.format("Error releasing exclusive lock on the manifest file %s for %s (%s) in %s", manifestFilePath, caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a deleted item flag for the case.
|
||||
*
|
||||
* @param flag The flag to set.
|
||||
*/
|
||||
private void setDeletedItemFlag(CaseNodeData.DeletedFlags flag) {
|
||||
try {
|
||||
caseNodeData.setDeletedFlag(flag);
|
||||
coordinationService.setNodeData(CategoryNode.CASES, caseDirectoryPath.toString(), caseNodeData.toArray());
|
||||
} catch (IOException | CoordinationServiceException | InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error updating deleted item flag %s for %s (%s) in %s", flag.name(), caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user