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.deleteCaseFailureMessageBox.title=Failed to Delete Case
|
||||||
Case.exceptionMessage.cancelledByUser=Cancelled by user.
|
Case.exceptionMessage.cancelledByUser=Cancelled by user.
|
||||||
Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.
|
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.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.
|
Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case.
|
||||||
# {0} - exception message
|
# {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.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}.
|
||||||
Case.exceptionMessage.emptyCaseDir=Must specify a case directory path.
|
Case.exceptionMessage.emptyCaseDir=Must specify a case directory path.
|
||||||
Case.exceptionMessage.emptyCaseName=Must specify a case name.
|
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
|
# {0} - exception message
|
||||||
Case.exceptionMessage.execExceptionWrapperMessage={0}
|
Case.exceptionMessage.execExceptionWrapperMessage={0}
|
||||||
# {0} - exception message
|
# {0} - exception message
|
||||||
@ -58,36 +58,12 @@ Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...
|
|||||||
Case.progressMessage.creatingCaseDatabase=Creating case database...
|
Case.progressMessage.creatingCaseDatabase=Creating case database...
|
||||||
Case.progressMessage.creatingCaseDirectory=Creating case directory...
|
Case.progressMessage.creatingCaseDirectory=Creating case directory...
|
||||||
Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...
|
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.deletingCaseDatabase=Deleting case database...
|
||||||
Case.progressMessage.deletingCaseDirectory=Deleting case directory...
|
Case.progressMessage.deletingCaseDirectory=Deleting case directory...
|
||||||
Case.progressMessage.deletingDirectoryCoordinationServiceNode=Deleting case directory lock coordination service node...
|
Case.progressMessage.deletingCaseDirLockNode=Deleting case directory lock coordination service node...
|
||||||
Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resourceslock coordination service node...
|
Case.progressMessage.deletingResourcesLockNode=Deleting case resources lock node...
|
||||||
Case.progressMessage.deletingTextIndex=Deleting text index...
|
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.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.openingApplicationServiceResources=Opening application service case resources...
|
||||||
Case.progressMessage.openingCaseDatabase=Opening case database...
|
Case.progressMessage.openingCaseDatabase=Opening case database...
|
||||||
Case.progressMessage.openingCaseLevelServices=Opening case-level services...
|
Case.progressMessage.openingCaseLevelServices=Opening case-level services...
|
||||||
|
@ -702,8 +702,45 @@ public class Case {
|
|||||||
}
|
}
|
||||||
CaseMetadata metadata = currentCase.getMetadata();
|
CaseMetadata metadata = currentCase.getMetadata();
|
||||||
closeCurrentCase();
|
closeCurrentCase();
|
||||||
ProgressIndicator progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
|
deleteCase(metadata);
|
||||||
deleteCase(metadata, true, progressIndicator);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
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.
|
* Deletes a single-user case.
|
||||||
*
|
*
|
||||||
@ -925,12 +928,12 @@ public class Case {
|
|||||||
* @param progressIndicator A progress indicator.
|
* @param progressIndicator A progress indicator.
|
||||||
*
|
*
|
||||||
* @throws CaseActionException If there were one or more errors deleting the
|
* @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({
|
@Messages({
|
||||||
"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.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}."
|
|
||||||
})
|
})
|
||||||
private static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
|
private static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
|
||||||
boolean errorsOccurred = false;
|
boolean errorsOccurred = false;
|
||||||
@ -938,16 +941,14 @@ public class Case {
|
|||||||
deleteTextIndex(metadata, progressIndicator);
|
deleteTextIndex(metadata, progressIndicator);
|
||||||
} catch (KeywordSearchServiceException ex) {
|
} catch (KeywordSearchServiceException ex) {
|
||||||
errorsOccurred = true;
|
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);
|
logger.log(Level.WARNING, 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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
deleteCaseDirectory(metadata, progressIndicator);
|
deleteCaseDirectory(metadata, progressIndicator);
|
||||||
} catch (CaseActionException ex) {
|
} catch (CaseActionException ex) {
|
||||||
errorsOccurred = true;
|
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);
|
logger.log(Level.WARNING, 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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteFromRecentCases(metadata, progressIndicator);
|
deleteFromRecentCases(metadata, progressIndicator);
|
||||||
@ -961,61 +962,37 @@ public class Case {
|
|||||||
* Deletes a multi-user case.
|
* Deletes a multi-user case.
|
||||||
*
|
*
|
||||||
* @param metadata The case metadata.
|
* @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.
|
* @param progressIndicator A progress indicator.
|
||||||
*
|
*
|
||||||
* @throws CaseActionException If there were one or more errors deleting the
|
* @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({
|
@Messages({
|
||||||
"Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...",
|
"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.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.",
|
"Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.",
|
||||||
"# {0} - exception message", "Case.progressMessage.errorLockingCaseName=An error occurred exclusively locking the case name for deletion (see log for details): {0}.",
|
"Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case..."
|
||||||
"# {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}."
|
|
||||||
})
|
})
|
||||||
private static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
|
private static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
|
||||||
CaseNodeData caseNodeData;
|
|
||||||
boolean errorsOccurred = false;
|
|
||||||
progressIndicator.progress(Bundle.Case_progressMessage_connectingToCoordSvc());
|
progressIndicator.progress(Bundle.Case_progressMessage_connectingToCoordSvc());
|
||||||
CoordinationService coordinationService;
|
CoordinationService coordinationService;
|
||||||
try {
|
try {
|
||||||
coordinationService = CoordinationService.getInstance();
|
coordinationService = CoordinationService.getInstance();
|
||||||
} catch (CoordinationServiceException ex) {
|
} 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);
|
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());
|
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
CaseNodeData caseNodeData;
|
||||||
* Acquire an exclusive case name lock. This will prevent auto ingest
|
boolean errorsOccurred = false;
|
||||||
* 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());
|
|
||||||
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())) {
|
try (CoordinationService.Lock dirLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) {
|
||||||
if (dirLock == null) {
|
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()));
|
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()));
|
||||||
progressIndicator.progress(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
|
|
||||||
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
|
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1025,108 +1002,143 @@ public class Case {
|
|||||||
if (nodeBytes != null && nodeBytes.length > 0) {
|
if (nodeBytes != null && nodeBytes.length > 0) {
|
||||||
caseNodeData = new CaseNodeData(nodeBytes);
|
caseNodeData = new CaseNodeData(nodeBytes);
|
||||||
} else {
|
} else {
|
||||||
logger.log(Level.SEVERE, String.format("Missing coordination service node data %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
|
logger.log(Level.WARNING, 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());
|
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
||||||
}
|
}
|
||||||
} catch (CoordinationServiceException | InterruptedException | IOException ex) {
|
} 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);
|
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()));
|
|
||||||
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
errorsOccurred = deleteMultiUserCase(caseNodeData, metadata, progressIndicator);
|
||||||
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) {
|
} 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);
|
logger.log(Level.SEVERE, String.format("Error exclusively locking the case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
|
||||||
progressIndicator.progress(Bundle.Case_progressMessage_errorLockingCase(ex.getMessage()));
|
|
||||||
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
} 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()));
|
|
||||||
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteCaseResourcesCoordinationServiceNode(metadata, progressIndicator, coordinationService);
|
|
||||||
deleteCaseAutoIngestLogCoordinationServiceNode(metadata, progressIndicator, coordinationService);
|
|
||||||
// RJCTODO: Delete case name lock node
|
|
||||||
|
|
||||||
if (!errorsOccurred) {
|
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 {
|
try {
|
||||||
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), caseNodeData.toArray());
|
deleteCaseDirectoryLockNode(caseNodeData, progressIndicator);
|
||||||
} catch (IOException | CoordinationServiceException | InterruptedException ex) {
|
} catch (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);
|
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);
|
||||||
progressIndicator.progress(Bundle.Case_progressMessage_errorSavingDeletedItemsFlags(ex.getMessage()));
|
errorsOccurred = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorsOccurred) {
|
||||||
throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
|
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 metadata The case metadata.
|
||||||
* @param progressIndicator A progress indicator.
|
* @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.
|
* 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.
|
* 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
|
* to drop the database from the database
|
||||||
* server.
|
* server.
|
||||||
|
* @throws InterruptedException If interrupted while blocked waiting for
|
||||||
|
* coordination service data to be written
|
||||||
|
* to the coordination service node
|
||||||
|
* database.
|
||||||
*/
|
*/
|
||||||
@Messages({
|
@Messages({
|
||||||
"Case.progressMessage.deletingCaseDatabase=Deleting case database...",})
|
"Case.progressMessage.deletingCaseDatabase=Deleting case database..."
|
||||||
private static void deleteCaseDatabase(CaseMetadata metadata, ProgressIndicator progressIndicator,
|
})
|
||||||
CaseNodeData caseNodeData) throws UserPreferencesException, ClassNotFoundException, SQLException {
|
private static void deleteCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator) throws UserPreferencesException, ClassNotFoundException, SQLException, InterruptedException {
|
||||||
if (caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DB)) {
|
if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DB)) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
|
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
|
||||||
CaseDbConnectionInfo db;
|
CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo();
|
||||||
db = UserPreferences.getDatabaseConnectionInfo();
|
String url = "jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres"; //NON-NLS
|
||||||
Class.forName("org.postgresql.Driver"); //NON-NLS
|
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
|
try (Connection connection = DriverManager.getConnection(url, info.getUserName(), info.getPassword()); Statement statement = connection.createStatement()) {
|
||||||
Statement statement = connection.createStatement();) {
|
|
||||||
String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
|
String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
|
||||||
statement.execute(deleteCommand);
|
statement.execute(deleteCommand);
|
||||||
}
|
}
|
||||||
caseNodeData.setDeletedFlag(CaseNodeData.DeletedFlags.CASE_DB);
|
setDeletedItemFlag(caseNodeData, CaseNodeData.DeletedFlags.CASE_DB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1151,19 +1163,21 @@ public class Case {
|
|||||||
/**
|
/**
|
||||||
* Attempts to delete the text index for a multi-user 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 metadata The case mnetadata.
|
||||||
* @param progressIndicator A progress indicator.
|
* @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
|
* @throws KeywordSearchServiceException If there is an error deleting the
|
||||||
* text index.
|
* 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)) {
|
if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.TEXT_INDEX)) {
|
||||||
deleteTextIndex(metadata, progressIndicator);
|
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..."
|
"Case.progressMessage.deletingCaseDirectory=Deleting case directory..."
|
||||||
})
|
})
|
||||||
private static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
|
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());
|
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
|
||||||
// RJCTODO: Use robocopy on Windows, possibly just for longer paths
|
|
||||||
if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
|
if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
|
||||||
throw new CaseActionException(String.format("Failed to delete %s", 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 metadata The case mnetadata.
|
||||||
* @param progressIndicator A progress indicator.
|
* @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
|
* @throws CaseActionException if there is an error deleting the case
|
||||||
* directory.
|
* 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)) {
|
if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DIR)) {
|
||||||
deleteCaseDirectory(metadata, progressIndicator);
|
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.
|
* Deletes the case resources lock coordination service node for a
|
||||||
* @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
|
|
||||||
* multi-user case.
|
* 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
|
* @param progressIndicator The progress indicator for the deletion
|
||||||
* operation.
|
* 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({
|
@Messages({
|
||||||
"Case.progressMessage.deletingDirectoryCoordinationServiceNode=Deleting case directory lock coordination service node...",
|
"Case.progressMessage.deletingResourcesLockNode=Deleting case resources lock node..."
|
||||||
"# {0} - exception message", "Case.progressMessage.errorDeletingCaseDirLockNode=An error occurred deleting the case directory lock node (see log for details): {0}."
|
|
||||||
})
|
})
|
||||||
static void deleteCaseDirectoryCoordinationServiceNode(CaseMetadata metadata, ProgressIndicator progressIndicator, CoordinationService coordinationService) {
|
@Beta
|
||||||
String caseDirectoryLockNodePath = metadata.getCaseDirectory();
|
private static void deleteCaseResourcesLockNode(CaseNodeData caseNodeData, ProgressIndicator progressIndicator) throws CoordinationServiceException, InterruptedException {
|
||||||
try {
|
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);
|
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()));
|
/**
|
||||||
|
* 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 {
|
||||||
|
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);
|
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_DB(2),
|
||||||
CASE_DIR(4),
|
CASE_DIR(4),
|
||||||
DATA_SOURCES(8),
|
DATA_SOURCES(8),
|
||||||
JOB_MANIFEST_NODES(16);
|
MANIFEST_FILE_LOCK_NODES(16);
|
||||||
|
|
||||||
private final short value;
|
private final short value;
|
||||||
|
|
||||||
|
@ -29,76 +29,60 @@ import java.util.List;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
||||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||||
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries the coordination service to collect the multi-user case node data
|
* Queries the coordination service to collect the multi-user case node data
|
||||||
* stored in the case directory lock ZooKeeper nodes.
|
* 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 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
|
* Queries the coordination service to collect the multi-user case node data
|
||||||
* stored in the case directory lock ZooKeeper nodes.
|
* stored in the case directory lock ZooKeeper nodes.
|
||||||
*
|
*
|
||||||
* @return A list of CaseNodedata objects that convert data for a case
|
* @return The node data for the multi-user cases known to the coordination
|
||||||
* directory lock coordination service node to and from byte arrays.
|
* 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 List<CaseNodeData> cases = new ArrayList<>();
|
||||||
final CoordinationService coordinationService = CoordinationService.getInstance();
|
final CoordinationService coordinationService = CoordinationService.getInstance();
|
||||||
final List<String> nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
|
final List<String> nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
|
||||||
for (String nodeName : nodeList) {
|
for (String nodeName : nodeList) {
|
||||||
/*
|
if (CaseCoordinationServiceUtils.isCaseLockName(nodeName)
|
||||||
* Ignore auto ingest case name lock nodes.
|
|| CaseCoordinationServiceUtils.isCaseResourcesLockName(nodeName)
|
||||||
*/
|
|| CaseCoordinationServiceUtils.isCaseAutoIngestLogLockName(nodeName)) {
|
||||||
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)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the data from the case directory lock node. This data may not
|
* 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 {
|
try {
|
||||||
CaseNodeData nodeData;
|
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) {
|
if (nodeBytes != null && nodeBytes.length > 0) {
|
||||||
nodeData = new CaseNodeData(nodeBytes);
|
nodeData = new CaseNodeData(nodeBytes);
|
||||||
if (nodeData.getVersion() == 0) {
|
if (nodeData.getVersion() < CaseNodeData.getCurrentVersion()) {
|
||||||
/*
|
nodeData = updateNodeData(nodeName, nodeData);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
} 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);
|
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
|
* Updates the case directory lock coordination service node data for a
|
||||||
* the metadata file for the case associated with the node.
|
* case.
|
||||||
*
|
*
|
||||||
* @param nodeName The coordination service node name, i.e., the case
|
* @param nodeName The coordination service node name, i.e., the case
|
||||||
* directory path.
|
* directory path.
|
||||||
* @param errorsOccurred Whether or not errors occurred during an auto
|
* @param oldNodeData .
|
||||||
* ingest job for the case.
|
|
||||||
*
|
*
|
||||||
* @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
|
* @throws IOException If there is an error writing the
|
||||||
* node data to a byte array.
|
* node data to a byte array.
|
||||||
@ -130,28 +114,67 @@ final public class MultiUserCaseNodeDataCollector {
|
|||||||
* @throws InterruptedException If a coordination service operation
|
* @throws InterruptedException If a coordination service operation
|
||||||
* is interrupted.
|
* is interrupted.
|
||||||
*/
|
*/
|
||||||
private static CaseNodeData createNodeDataFromCaseMetadata(String nodeName, boolean errorsOccurred) throws IOException, CaseMetadata.CaseMetadataException, ParseException, CoordinationService.CoordinationServiceException, InterruptedException {
|
private static CaseNodeData updateNodeData(String nodeName, CaseNodeData oldNodeData) throws IOException, CaseMetadata.CaseMetadataException, ParseException, CoordinationService.CoordinationServiceException, InterruptedException {
|
||||||
CaseNodeData nodeData = null;
|
|
||||||
Path caseDirectoryPath = Paths.get(nodeName).toRealPath(LinkOption.NOFOLLOW_LINKS);
|
Path caseDirectoryPath = Paths.get(nodeName).toRealPath(LinkOption.NOFOLLOW_LINKS);
|
||||||
File caseDirectory = caseDirectoryPath.toFile();
|
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();
|
File[] files = caseDirectory.listFiles();
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
String name = file.getName().toLowerCase();
|
String name = file.getName().toLowerCase();
|
||||||
if (name.endsWith(CaseMetadata.getFileExtension())) {
|
if (name.endsWith(CaseMetadata.getFileExtension())) {
|
||||||
CaseMetadata metadata = new CaseMetadata(Paths.get(file.getAbsolutePath()));
|
CaseMetadata metadata = new CaseMetadata(Paths.get(file.getAbsolutePath()));
|
||||||
nodeData = new CaseNodeData(metadata);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeData != null) {
|
if (nodeData != null) {
|
||||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
CoordinationService.getInstance().setNodeData(CoordinationService.CategoryNode.CASES, nodeName, nodeData.toArray());
|
||||||
coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodeName, nodeData.toArray());
|
}
|
||||||
|
|
||||||
return nodeData;
|
return nodeData;
|
||||||
} else {
|
}
|
||||||
throw new IOException(String.format("Could not find case metadata file for %s", nodeName));
|
|
||||||
|
/**
|
||||||
|
* 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.createTime=Create Time
|
||||||
|
MultiUserCaseBrowserCustomizer.column.dataSourcesDeleteStatus=Data Sources Deleted
|
||||||
MultiUserCaseBrowserCustomizer.column.directory=Directory
|
MultiUserCaseBrowserCustomizer.column.directory=Directory
|
||||||
MultiUserCaseBrowserCustomizer.column.displayName=Name
|
MultiUserCaseBrowserCustomizer.column.displayName=Name
|
||||||
MultiUserCaseBrowserCustomizer.column.lastAccessTime=Last Access Time
|
MultiUserCaseBrowserCustomizer.column.lastAccessTime=Last Access Time
|
||||||
|
MultiUserCaseBrowserCustomizer.column.manifestCoordSvcNodesDeleteStatus=Manifest ZooKeeper Node Deleted
|
||||||
|
MultiUserCaseBrowserCustomizer.column.textIndexDeleteStatus=Text Index Deleted
|
||||||
MultiUserCasesBrowserPanel.waitNode.message=Please Wait...
|
MultiUserCasesBrowserPanel.waitNode.message=Please Wait...
|
||||||
|
@ -141,14 +141,23 @@ public interface MultiUserCaseBrowserCustomizer {
|
|||||||
"MultiUserCaseBrowserCustomizer.column.displayName=Name",
|
"MultiUserCaseBrowserCustomizer.column.displayName=Name",
|
||||||
"MultiUserCaseBrowserCustomizer.column.createTime=Create Time",
|
"MultiUserCaseBrowserCustomizer.column.createTime=Create Time",
|
||||||
"MultiUserCaseBrowserCustomizer.column.directory=Directory",
|
"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 {
|
public enum Column {
|
||||||
DISPLAY_NAME(Bundle.MultiUserCaseBrowserCustomizer_column_displayName()),
|
DISPLAY_NAME(Bundle.MultiUserCaseBrowserCustomizer_column_displayName()),
|
||||||
CREATE_DATE(Bundle.MultiUserCaseBrowserCustomizer_column_createTime()),
|
CREATE_DATE(Bundle.MultiUserCaseBrowserCustomizer_column_createTime()),
|
||||||
DIRECTORY(Bundle.MultiUserCaseBrowserCustomizer_column_directory()),
|
DIRECTORY(Bundle.MultiUserCaseBrowserCustomizer_column_directory()),
|
||||||
LAST_ACCESS_DATE(Bundle.MultiUserCaseBrowserCustomizer_column_lastAccessTime());
|
LAST_ACCESS_DATE(Bundle.MultiUserCaseBrowserCustomizer_column_lastAccessTime()),
|
||||||
// RJCTODO: Add properties for deleted items flags
|
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;
|
private final String displayName;
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ final class MultiUserCasesRootNode extends AbstractNode {
|
|||||||
try {
|
try {
|
||||||
List<CaseNodeData> caseNodeData = MultiUserCaseNodeDataCollector.getNodeData();
|
List<CaseNodeData> caseNodeData = MultiUserCaseNodeDataCollector.getNodeData();
|
||||||
keys.addAll(caseNodeData);
|
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);
|
logger.log(Level.SEVERE, "Failed to get case node data from coodination service", ex);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -365,15 +365,22 @@ public final class CoordinationService {
|
|||||||
*
|
*
|
||||||
* @throws CoordinationServiceException If there is an error deleting the
|
* @throws CoordinationServiceException If there is an error deleting the
|
||||||
* node.
|
* 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);
|
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
|
||||||
try {
|
try {
|
||||||
curator.delete().forPath(fullNodePath);
|
curator.delete().forPath(fullNodePath);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
if (ex instanceof InterruptedException) {
|
||||||
|
throw (InterruptedException) ex;
|
||||||
|
} else {
|
||||||
throw new CoordinationServiceException(String.format("Failed to delete node %s", fullNodePath), ex);
|
throw new CoordinationServiceException(String.format("Failed to delete node %s", fullNodePath), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a list of the child nodes of a category in the namespace.
|
* Gets a list of the child nodes of a category in the namespace.
|
||||||
@ -384,15 +391,22 @@ public final class CoordinationService {
|
|||||||
*
|
*
|
||||||
* @throws CoordinationServiceException If there is an error getting the
|
* @throws CoordinationServiceException If there is an error getting the
|
||||||
* node list.
|
* 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 {
|
try {
|
||||||
List<String> list = curator.getChildren().forPath(categoryNodeToPath.get(category.getDisplayName()));
|
List<String> list = curator.getChildren().forPath(categoryNodeToPath.get(category.getDisplayName()));
|
||||||
return list;
|
return list;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
if (ex instanceof InterruptedException) {
|
||||||
|
throw (InterruptedException) ex;
|
||||||
|
} else {
|
||||||
throw new CoordinationServiceException(String.format("Failed to get node list for %s", category.getDisplayName()), ex);
|
throw new CoordinationServiceException(String.format("Failed to get node list for %s", category.getDisplayName()), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a node path within a given category.
|
* Creates a node path within a given category.
|
||||||
@ -404,9 +418,9 @@ public final class CoordinationService {
|
|||||||
*/
|
*/
|
||||||
private String getFullyQualifiedNodePath(CategoryNode category, String nodePath) {
|
private String getFullyQualifiedNodePath(CategoryNode category, String nodePath) {
|
||||||
// nodePath on Unix systems starts with a "/" and ZooKeeper doesn't like two slashes in a row
|
// 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();
|
return categoryNodeToPath.get(category.getDisplayName()) + nodePath.toUpperCase();
|
||||||
}else{
|
} else {
|
||||||
return categoryNodeToPath.get(category.getDisplayName()) + "/" + nodePath.toUpperCase();
|
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()});
|
sysLogger.log(Level.INFO, "Opening case {0} for {1}", new Object[]{caseName, manifest.getFilePath()});
|
||||||
currentJob.setProcessingStage(AutoIngestJob.Stage.OPENING_CASE, Date.from(Instant.now()));
|
currentJob.setProcessingStage(AutoIngestJob.Stage.OPENING_CASE, Date.from(Instant.now()));
|
||||||
/*
|
/*
|
||||||
* Acquire and hold a case name lock so that only one node at as
|
* Acquire and hold a case name lock so that only one node at a time
|
||||||
* time can scan the output directory at a time. This prevents
|
* can search the output directory for an existing case. This
|
||||||
* making duplicate cases for the same auto ingest case.
|
* prevents making duplicate cases for the same auto ingest case.
|
||||||
*/
|
*/
|
||||||
try (Lock caseLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, caseName, 30, TimeUnit.MINUTES)) {
|
try (Lock caseLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, caseName, 30, TimeUnit.MINUTES)) {
|
||||||
if (null != caseLock) {
|
if (null != caseLock) {
|
||||||
|
@ -96,7 +96,7 @@ final class AutoIngestMetricsCollector {
|
|||||||
|
|
||||||
return newMetricsSnapshot;
|
return newMetricsSnapshot;
|
||||||
|
|
||||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
} catch (CoordinationService.CoordinationServiceException | InterruptedException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Failed to get node list from coordination service", ex);
|
LOGGER.log(Level.SEVERE, "Failed to get node list from coordination service", ex);
|
||||||
return new MetricsSnapshot();
|
return new MetricsSnapshot();
|
||||||
}
|
}
|
||||||
|
@ -378,7 +378,7 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
|
|||||||
|
|
||||||
return newJobsSnapshot;
|
return newJobsSnapshot;
|
||||||
|
|
||||||
} catch (CoordinationServiceException ex) {
|
} catch (CoordinationServiceException | InterruptedException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Failed to get node list from coordination service", ex);
|
LOGGER.log(Level.SEVERE, "Failed to get node list from coordination service", ex);
|
||||||
return new JobsSnapshot();
|
return new JobsSnapshot();
|
||||||
}
|
}
|
||||||
|
@ -180,26 +180,31 @@ DeleteCaseInputAndOutputAction.taskName=input-and-output
|
|||||||
DeleteCaseOutputAction.menuItemText=Delete Output
|
DeleteCaseOutputAction.menuItemText=Delete Output
|
||||||
DeleteCaseOutputAction.progressDisplayName=Delete Output
|
DeleteCaseOutputAction.progressDisplayName=Delete Output
|
||||||
DeleteCaseOutputAction.taskName=output
|
DeleteCaseOutputAction.taskName=output
|
||||||
DeleteCaseTask.progress.acquiringCaseDirLock=Acquiring exclusive case directory lock
|
DeleteCaseTask.progress.acquiringCaseDirLock=Acquiring an exclusive case directory lock...
|
||||||
DeleteCaseTask.progress.acquiringCaseNameLock=Acquiring exclusive case name lock
|
DeleteCaseTask.progress.acquiringCaseNameLock=Acquiring an exclusive case name lock...
|
||||||
DeleteCaseTask.progress.acquiringInputDirLocks=Acquiring exclusive input directory locks
|
DeleteCaseTask.progress.acquiringInputDirLocks=Acquiring exclusive input directory locks
|
||||||
DeleteCaseTask.progress.connectingToCoordSvc=Connecting to the coordination service
|
DeleteCaseTask.progress.acquiringManifestFileLocks=Acquiring exclusive manifest file locks...
|
||||||
DeleteCaseTask.progress.deletingCaseOutput=Deleting case output
|
DeleteCaseTask.progress.acquiringManifestLocks=Acquiring exclusive manifest file locks...
|
||||||
DeleteCaseTask.progress.deletingDirLockNode=Deleting case directory lock node
|
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
|
# {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.deletingInputDirLockNodes=Deleting input directory lock nodes
|
||||||
DeleteCaseTask.progress.deletingInputDirs=Deleting input directory
|
DeleteCaseTask.progress.deletingInputDirs=Deleting input directory...
|
||||||
DeleteCaseTask.progress.deletingJobLogLockNode=Deleting auto ingest job log lock node
|
DeleteCaseTask.progress.deletingJobLogLockNode=Deleting case auto ingest job log lock node...
|
||||||
DeleteCaseTask.progress.deletingNameLockNode=Deleting case name 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.deletingResourcesLockNode=Deleting case resources lock node
|
||||||
DeleteCaseTask.progress.gettingJobNodeData=Getting node data for auto ingest jobs
|
DeleteCaseTask.progress.gettingJobNodeData=Getting node data for auto ingest jobs...
|
||||||
DeleteCaseTask.progress.locatingCaseMetadataFile=Locating case metadata file
|
DeleteCaseTask.progress.locatingCaseMetadataFile=Locating case metadata file...
|
||||||
# {0} - input directory name
|
DeleteCaseTask.progress.lockingInputDir=Acquiring exclusive lock on manifest {0}
|
||||||
DeleteCaseTask.progress.lockingInputDir=Acquiring exclusive lock on input directory {0}
|
# {0} - manifest file name
|
||||||
|
DeleteCaseTask.progress.lockingManifestFile=Acquiring exclusive lock on manifest {0}...
|
||||||
# {0} - manifest file path
|
# {0} - manifest file path
|
||||||
DeleteCaseTask.progress.releasingManifestLock=Releasing the exclusive lock on manifest file {0}
|
DeleteCaseTask.progress.releasingManifestLock=Releasing the exclusive lock on manifest file {0}...
|
||||||
DeleteCaseTask.progress.releasingManifestLocks=Acquiring exclusive manifest file locks
|
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)
|
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
|
HINT_CasesDashboardTopComponent=This is an adminstrative dashboard for multi-user cases
|
||||||
OpenAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted.
|
OpenAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted.
|
||||||
|
@ -38,19 +38,24 @@ final class DeleteCaseInputAndOutputTask extends DeleteCaseTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void deleteWhileHoldingAllLocks() {
|
void deleteWhileHoldingAllLocks() throws InterruptedException {
|
||||||
deleteInputDirectories();
|
deleteInputDirectories();
|
||||||
deleteCaseOutput();
|
deleteCaseOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void deleteAfterCaseLocksReleased() {
|
void deleteAfterManifestLocksReleased() throws InterruptedException {
|
||||||
deleteCaseLockNodes();
|
deleteManifestFileLockNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void deleteAfterAllLocksReleased() {
|
void deleteAfterCaseDirectoryLockReleased() throws InterruptedException {
|
||||||
deleteInputDirectoryLockNodes();
|
this.deleteCaseDirectoryLockNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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
|
* 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
|
* 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
|
* (manifest file lock nodes) and the rest of the case intact. The use case is
|
||||||
* retaining the option to restore the input directories, effectively restoring
|
* freeing space while retaining the option to restore the input directories,
|
||||||
* the case.
|
* effectively restoring the case.
|
||||||
*/
|
*/
|
||||||
final class DeleteCaseInputTask extends DeleteCaseTask {
|
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
|
* case produced via auto ingest, while leaving the auto ingest job
|
||||||
* coordination service nodes and the rest of the case intact. The use case
|
* coordination service nodes (manifest file lock nodes) and the rest of the
|
||||||
* is freeing space while retaining the option to restore the input
|
* case intact. The use case is freeing space while retaining the option to
|
||||||
* directories, effectively restoring the case.
|
* restore the input directories, effectively restoring the case.
|
||||||
*
|
*
|
||||||
* @param caseNodeData The case directory lock coordination service node
|
* @param caseNodeData The case directory lock coordination service node
|
||||||
* data for the case.
|
* data for the case.
|
||||||
@ -46,16 +46,20 @@ final class DeleteCaseInputTask extends DeleteCaseTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void deleteWhileHoldingAllLocks() {
|
void deleteWhileHoldingAllLocks() throws InterruptedException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void deleteAfterCaseDirectoryLockReleased() throws InterruptedException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void deleteAfterCaseNameLockReleased() throws InterruptedException {
|
||||||
deleteInputDirectories();
|
deleteInputDirectories();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void deleteAfterCaseLocksReleased() {
|
void deleteAfterManifestLocksReleased() throws InterruptedException {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void deleteAfterAllLocksReleased() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.experimental.autoingest;
|
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.NbBundle;
|
||||||
import org.openide.util.Utilities;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
|
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
|
||||||
import org.sleuthkit.autopsy.progress.ProgressIndicator;
|
import org.sleuthkit.autopsy.progress.ProgressIndicator;
|
||||||
|
|
||||||
|
@ -45,17 +45,22 @@ final class DeleteCaseOutputTask extends DeleteCaseTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void deleteWhileHoldingAllLocks() {
|
void deleteWhileHoldingAllLocks() throws InterruptedException {
|
||||||
deleteCaseOutput();
|
deleteCaseOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void deleteAfterCaseLocksReleased() {
|
void deleteAfterCaseDirectoryLockReleased() throws InterruptedException {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void deleteAfterAllLocksReleased() {
|
void deleteAfterManifestLocksReleased() throws InterruptedException {
|
||||||
deleteInputDirectoryLockNodes();
|
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;
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -29,138 +30,74 @@ import java.util.logging.Level;
|
|||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.CaseActionException;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
||||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
|
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;
|
||||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode;
|
||||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||||
import org.sleuthkit.autopsy.progress.ProgressIndicator;
|
import org.sleuthkit.autopsy.progress.ProgressIndicator;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
|
|
||||||
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobNodeData.InvalidDataException;
|
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobNodeData.InvalidDataException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base class for tasks that delete part or all of a case produced via auto
|
* A base class for tasks that delete part or all of a given case.
|
||||||
* ingest.
|
|
||||||
*/
|
*/
|
||||||
abstract class DeleteCaseTask implements Runnable {
|
abstract class DeleteCaseTask implements Runnable {
|
||||||
|
|
||||||
private static final String RESOURCES_LOCK_SUFFIX = "_resources"; //NON-NLS
|
|
||||||
private static final Logger logger = AutoIngestDashboardLogger.getLogger();
|
private static final Logger logger = AutoIngestDashboardLogger.getLogger();
|
||||||
private final CaseNodeData caseNodeData;
|
private final CaseNodeData caseNodeData;
|
||||||
|
private final String caseDisplayName;
|
||||||
|
private final String caseUniqueName;
|
||||||
|
private final Path caseDirectoryPath;
|
||||||
private final ProgressIndicator progress;
|
private final ProgressIndicator progress;
|
||||||
private final List<AutoIngestJobNodeData> nodeDataForAutoIngestJobs;
|
private final List<AutoIngestJobNodeData> nodeDataForAutoIngestJobs;
|
||||||
private final Map<Path, CoordinationService.Lock> manifestFileLocks;
|
private final Map<String, CoordinationService.Lock> manifestFileLocks;
|
||||||
private CoordinationService coordinationService;
|
private CoordinationService coordinationService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the base class part of a task that deletes part or all of a
|
* 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
|
* @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.
|
* @param progress A progress indicator.
|
||||||
*/
|
*/
|
||||||
DeleteCaseTask(CaseNodeData caseNodeData, ProgressIndicator progress) {
|
DeleteCaseTask(CaseNodeData caseNodeData, ProgressIndicator progress) {
|
||||||
this.caseNodeData = caseNodeData;
|
this.caseNodeData = caseNodeData;
|
||||||
this.progress = progress;
|
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.nodeDataForAutoIngestJobs = new ArrayList<>();
|
||||||
this.manifestFileLocks = new HashMap<>();
|
this.manifestFileLocks = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"DeleteCaseTask.progress.connectingToCoordSvc=Connecting to the coordination service",
|
"DeleteCaseTask.progress.startMessage=Preparing for deletion..."
|
||||||
"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"
|
|
||||||
})
|
})
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
progress.start(Bundle.DeleteCaseTask_progress_connectingToCoordSvc());
|
progress.start(Bundle.DeleteCaseTask_progress_startMessage());
|
||||||
try {
|
logger.log(Level.INFO, String.format("Beginning deletion of %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||||
coordinationService = CoordinationService.getInstance();
|
deleteCase();
|
||||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
logger.log(Level.SEVERE, String.format("Deletion of %s (%s) in %s completed", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||||
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();
|
|
||||||
|
|
||||||
} catch (Throwable ex) {
|
} 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 {
|
} finally {
|
||||||
progress.finish();
|
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
|
* Deletes part or all of the given case.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"DeleteCaseTask.progress.deletingInputDirs=Deleting input directory",
|
"DeleteCaseTask.progress.connectingToCoordSvc=Connecting to the coordination service...",
|
||||||
"# {0} - input directory name", "DeleteCaseTask.progress.deletingInputDir=Deleting input directory {0}"
|
"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..."
|
||||||
|
})
|
||||||
|
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 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({
|
||||||
|
"# {0} - input directory name", "DeleteCaseTask.progress.deletingInputDir=Deleting input directory {0}..."
|
||||||
})
|
})
|
||||||
protected void deleteInputDirectories() {
|
protected void deleteInputDirectories() {
|
||||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingInputDirs());
|
boolean allInputDirsDeleted = true;
|
||||||
logger.log(Level.INFO, String.format("Deleting input directories for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
|
||||||
for (AutoIngestJobNodeData jobNodeData : nodeDataForAutoIngestJobs) {
|
for (AutoIngestJobNodeData jobNodeData : nodeDataForAutoIngestJobs) {
|
||||||
final Path inputDirPath = jobNodeData.getManifestFilePath().getParent();
|
Path inputDirPath = jobNodeData.getManifestFilePath().getParent();
|
||||||
|
File inputDir = inputDirPath.toFile();
|
||||||
|
if (inputDir.exists()) {
|
||||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingInputDir(inputDirPath));
|
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()));
|
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()))) {
|
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()));
|
logger.log(Level.WARNING, String.format("Failed to delete the input directory %s for %s (%s) in %s", inputDirPath, caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||||
// RJCTODO: Update deletion flags
|
allInputDirsDeleted = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (allInputDirsDeleted) {
|
||||||
|
setDeletedItemFlag(CaseNodeData.DeletedFlags.DATA_SOURCES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the case directory, the case database, and the text index for the
|
* Deletes the case database, the text index, and the case directory for the
|
||||||
* case. Should only be called when holding all of the exclusive locks for
|
* case. Intended to be called by subclasses, if required, in their
|
||||||
* the case.
|
* customization of the deleteWhileHoldingAllLocks step of the case deletion
|
||||||
|
* algorithm.
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"DeleteCaseTask.progress.locatingCaseMetadataFile=Locating case metadata file",
|
"DeleteCaseTask.progress.locatingCaseMetadataFile=Locating case metadata file...",
|
||||||
"DeleteCaseTask.progress.deletingCaseOutput=Deleting case output"
|
"DeleteCaseTask.progress.deletingCaseOutput=Deleting case database, text index, and directory...",
|
||||||
|
"DeleteCaseTask.progress.deletingJobLogLockNode=Deleting case auto ingest job log lock node..."
|
||||||
})
|
})
|
||||||
protected void deleteCaseOutput() {
|
protected void deleteCaseOutput() {
|
||||||
progress.progress(Bundle.DeleteCaseTask_progress_locatingCaseMetadataFile());
|
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()));
|
logger.log(Level.INFO, String.format("Locating metadata file for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||||
String metadataFilePath = null;
|
CaseMetadata caseMetadata = null;
|
||||||
final File caseDirectory = caseNodeData.getDirectory().toFile();
|
final File caseDirectory = caseDirectoryPath.toFile();
|
||||||
final File[] filesInDirectory = caseDirectory.listFiles();
|
final File[] filesInDirectory = caseDirectory.listFiles();
|
||||||
if (filesInDirectory != null) {
|
if (filesInDirectory != null) {
|
||||||
for (File file : filesInDirectory) {
|
for (File file : filesInDirectory) {
|
||||||
if (file.getName().toLowerCase().endsWith(CaseMetadata.getFileExtension()) && file.isFile()) {
|
if (file.getName().toLowerCase().endsWith(CaseMetadata.getFileExtension()) && file.isFile()) {
|
||||||
metadataFilePath = file.getPath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadataFilePath != 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 {
|
try {
|
||||||
Case.deleteCase(new CaseMetadata(Paths.get(metadataFilePath)), false, progress);
|
caseMetadata = new CaseMetadata(Paths.get(file.getPath()));
|
||||||
} catch (CaseMetadata.CaseMetadataException | CaseActionException ex) {
|
} catch (CaseMetadata.CaseMetadataException ex) {
|
||||||
// RJCTODO: Set delete flags?
|
logger.log(Level.WARNING, String.format("Error getting opening case metadata file for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||||
logger.log(Level.WARNING, String.format("Error deleting output for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (caseMetadata != null) {
|
||||||
* Deletes the case name, case directory, case resources, and case auto
|
progress.progress(Bundle.DeleteCaseTask_progress_deletingCaseOutput());
|
||||||
* ingest log for the case. Should only be called when holding the exclusive
|
logger.log(Level.INFO, String.format("Deleting output for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||||
* locks for all of the input directories for the case.
|
Case.deleteMultiUserCase(caseNodeData, caseMetadata, progress); // RJCTODO: Make this method throw the interrupted exception.
|
||||||
*/
|
} else {
|
||||||
@Messages({
|
logger.log(Level.WARNING, String.format("Failed to locate metadata file for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||||
"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());
|
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()));
|
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(caseNodeData.getDirectory());
|
Path logFilePath = AutoIngestJobLogger.getLogPath(caseDirectoryPath); //RJCTODO: USe util here
|
||||||
try {
|
try {
|
||||||
coordinationService.deleteNode(CategoryNode.CASES, logFilePath.toString());
|
coordinationService.deleteNode(CategoryNode.CASES, logFilePath.toString());
|
||||||
} catch (CoordinationServiceException ex) {
|
} 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);
|
logger.log(Level.WARNING, String.format("Error deleting case auto ingest job log lock node for %s (%s) in %s", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||||
// RJCTODO: Set delete flags
|
} catch (InterruptedException ex) {
|
||||||
}
|
logger.log(Level.INFO, String.format("Deletion of %s (%s) in %s cancelled", caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the input directory lock nodes for the case. Should only be
|
* Deletes the manifest file lock coordination service nodes for the case.
|
||||||
* called after releasing all of the locks for the case.
|
* Intended to be called by subclasses, if required, in their customization
|
||||||
|
* of the deleteAfterManifestLocksReleased step of the case deletion
|
||||||
|
* algorithm.
|
||||||
*/
|
*/
|
||||||
@Messages({
|
@Messages({
|
||||||
"DeleteCaseTask.progress.deletingInputDirLockNodes=Deleting input directory lock nodes"
|
"DeleteCaseTask.progress.deletingManifestFileLockNodes=Deleting manifest file lock nodes..."
|
||||||
})
|
})
|
||||||
protected void deleteInputDirectoryLockNodes() {
|
protected void deleteManifestFileLockNodes() throws InterruptedException {
|
||||||
progress.progress(Bundle.DeleteCaseTask_progress_deletingInputDirLockNodes());
|
boolean allInputDirsDeleted = true;
|
||||||
logger.log(Level.INFO, String.format("Deleting input directory lock nodes for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()));
|
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) {
|
for (AutoIngestJobNodeData jobNodeData : nodeDataForAutoIngestJobs) {
|
||||||
try {
|
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());
|
coordinationService.deleteNode(CoordinationService.CategoryNode.MANIFESTS, jobNodeData.getManifestFilePath().toString());
|
||||||
} catch (CoordinationServiceException ex) {
|
} catch (CoordinationServiceException ex) {
|
||||||
Path inputDirPath = jobNodeData.getManifestFilePath().getParent();
|
logger.log(Level.WARNING, String.format("Error deleting manifest file lock node %s for %s (%s) in %s", jobNodeData.getManifestFilePath(), caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||||
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);
|
allInputDirsDeleted = false;
|
||||||
// RJCTODO: Set delete flags
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
* Deletes the case name coordination service lock node for the case.
|
||||||
* service node data for a 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.
|
* @throws InterruptedException
|
||||||
*
|
|
||||||
* @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.
|
|
||||||
*/
|
*/
|
||||||
private void getAutoIngestJobNodeData() throws CoordinationServiceException {
|
@Messages({
|
||||||
String caseName = caseNodeData.getName();
|
"DeleteCaseTask.progress.deletingNameLockNode=Deleting case name lock node..." // RJCTODO: Use consistent terminology
|
||||||
final List<String> nodes = coordinationService.getNodeList(CoordinationService.CategoryNode.MANIFESTS);
|
})
|
||||||
for (String nodeName : nodes) {
|
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 {
|
try {
|
||||||
byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodeName);
|
byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodeName);
|
||||||
if (nodeBytes == null || nodeBytes.length <= 0) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(nodeBytes);
|
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(nodeBytes);
|
||||||
if (caseName.equals(nodeData.getCaseName())) {
|
if (caseName.equals(nodeData.getCaseName())) {
|
||||||
nodeDataForAutoIngestJobs.add(nodeData);
|
nodeDataForAutoIngestJobs.add(nodeData);
|
||||||
}
|
}
|
||||||
} catch (CoordinationService.CoordinationServiceException | InterruptedException | InvalidDataException ex) {
|
} catch (CoordinationService.CoordinationServiceException | InvalidDataException ex) {
|
||||||
logger.log(Level.WARNING, String.format("Failed to get coordination service node data for %s", nodeName), 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.
|
* Acquires either all or none of the manifest file 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.
|
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"# {0} - input directory name", "DeleteCaseTask.progress.lockingInputDir=Acquiring exclusive lock on input directory {0}",})
|
"# {0} - manifest file name", "DeleteCaseTask.progress.lockingManifestFile=Acquiring exclusive lock on manifest {0}..."
|
||||||
private void getInputDirectoryLocks() {
|
})
|
||||||
|
private void getManifestFileLocks() {
|
||||||
for (AutoIngestJobNodeData autoIngestJobNodeData : nodeDataForAutoIngestJobs) {
|
for (AutoIngestJobNodeData autoIngestJobNodeData : nodeDataForAutoIngestJobs) {
|
||||||
final Path inputDirPath = autoIngestJobNodeData.getManifestFilePath().getParent();
|
String manifestPath = autoIngestJobNodeData.getManifestFilePath().toString();
|
||||||
try {
|
try {
|
||||||
progress.progress(Bundle.DeleteCaseTask_progress_lockingInputDir(inputDirPath));
|
progress.progress(Bundle.DeleteCaseTask_progress_lockingManifestFile(manifestPath));
|
||||||
final CoordinationService.Lock inputDirLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.MANIFESTS, autoIngestJobNodeData.getManifestFilePath().toString());
|
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) {
|
if (null != inputDirLock) {
|
||||||
manifestFileLocks.put(autoIngestJobNodeData.getManifestFilePath(), inputDirLock);
|
manifestFileLocks.put(manifestPath, inputDirLock);
|
||||||
} else {
|
} 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()));
|
logger.log(Level.INFO, String.format("Failed to exclusively lock the manifest %s for %s (%s) in %s", manifestPath, caseDisplayName, caseUniqueName, caseDirectoryPath));
|
||||||
releaseInputDirectoryLocks();
|
releaseManifestFileLocks();
|
||||||
manifestFileLocks.clear();
|
manifestFileLocks.clear();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
} 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);
|
logger.log(Level.SEVERE, String.format("Error exclusively locking the manifest %s for %s (%s) in %s", manifestPath, caseDisplayName, caseUniqueName, caseDirectoryPath), ex);
|
||||||
releaseInputDirectoryLocks();
|
releaseManifestFileLocks();
|
||||||
manifestFileLocks.clear();
|
manifestFileLocks.clear();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,23 +501,36 @@ abstract class DeleteCaseTask implements Runnable {
|
|||||||
* for the case.
|
* for the case.
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({
|
@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()) {
|
if (!manifestFileLocks.isEmpty()) {
|
||||||
progress.progress(Bundle.DeleteCaseTask_progress_releasingManifestLocks());
|
for (Map.Entry<String, CoordinationService.Lock> entry : manifestFileLocks.entrySet()) {
|
||||||
for (Map.Entry<Path, CoordinationService.Lock> entry : manifestFileLocks.entrySet()) {
|
String manifestFilePath = entry.getKey();
|
||||||
final Path manifestFilePath = entry.getKey();
|
CoordinationService.Lock manifestFileLock = entry.getValue();
|
||||||
final CoordinationService.Lock manifestFileLock = entry.getValue();
|
|
||||||
try {
|
try {
|
||||||
progress.progress(Bundle.DeleteCaseTask_progress_releasingManifestLock(manifestFilePath));
|
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();
|
manifestFileLock.release();
|
||||||
} catch (CoordinationServiceException ex) {
|
} 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