130 @GuardedBy(
"IngestManager.class")
134 private final ExecutorService
startIngestJobsExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(
"IM-start-ingest-jobs-%d").build());
135 @GuardedBy(
"startIngestJobFutures")
137 @GuardedBy(
"ingestJobsById")
143 private final ExecutorService
eventPublishingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(
"IM-ingest-events-%d").build());
164 instance.subscribeToServiceMonitorEvents();
204 PropertyChangeListener propChangeListener = (PropertyChangeEvent evt) -> {
219 logger.log(Level.SEVERE,
"Service {0} is down, cancelling all running ingest jobs", serviceDisplayName);
221 EventQueue.invokeLater(
new Runnable() {
224 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
225 NbBundle.getMessage(
this.getClass(),
"IngestManager.cancellingIngest.msgDlg.text"),
226 NbBundle.getMessage(
this.getClass(),
"IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName),
227 JOptionPane.ERROR_MESSAGE);
241 Set<String> servicesList =
new HashSet<>();
253 if (event.getNewValue() != null) {
269 void handleCaseOpened() {
271 clearIngestMessageBox();
273 Case openedCase = Case.getCurrentCaseThrows();
274 String channelPrefix = openedCase.getName();
275 if (Case.CaseType.MULTI_USER_CASE == openedCase.getCaseType()) {
276 jobEventPublisher.openRemoteEventChannel(String.format(INGEST_JOB_EVENT_CHANNEL_NAME, channelPrefix));
277 moduleEventPublisher.openRemoteEventChannel(String.format(INGEST_MODULE_EVENT_CHANNEL_NAME, channelPrefix));
279 openedCase.getSleuthkitCase().registerForEvents(
this);
280 }
catch (NoCurrentCaseException | AutopsyEventException ex) {
281 logger.log(Level.SEVERE,
"Failed to open remote events channel", ex);
282 MessageNotifyUtil.Notify.error(NbBundle.getMessage(IngestManager.class,
"IngestManager.OpenEventChannel.Fail.Title"),
283 NbBundle.getMessage(IngestManager.class,
"IngestManager.OpenEventChannel.Fail.ErrMsg"));
294 void handleArtifactsPosted(Blackboard.ArtifactsPostedEvent tskEvent) {
299 List<DataArtifact> newDataArtifacts =
new ArrayList<>();
300 List<AnalysisResult> newAnalysisResults =
new ArrayList<>();
301 Collection<BlackboardArtifact> newArtifacts = tskEvent.getArtifacts();
302 for (BlackboardArtifact artifact : newArtifacts) {
303 if (artifact instanceof DataArtifact) {
304 newDataArtifacts.add((DataArtifact) artifact);
306 newAnalysisResults.add((AnalysisResult) artifact);
309 if (!newDataArtifacts.isEmpty() || !newAnalysisResults.isEmpty()) {
311 Optional<Long> ingestJobId = tskEvent.getIngestJobId();
312 if (ingestJobId.isPresent()) {
313 synchronized (ingestJobsById) {
314 ingestJob = ingestJobsById.get(ingestJobId.get());
371 BlackboardArtifact artifact = newArtifacts.iterator().next();
372 if (artifact !=
null) {
374 Content artifactDataSource = artifact.getDataSource();
375 synchronized (ingestJobsById) {
376 for (
IngestJob job : ingestJobsById.values()) {
377 Content dataSource = job.getDataSource();
378 if (artifactDataSource.getId() == dataSource.getId()) {
384 }
catch (TskCoreException ex) {
385 logger.log(Level.SEVERE, String.format(
"Failed to get data source for blackboard artifact (object ID = %d)", artifact.getId()), ex);
389 if (ingestJob !=
null) {
390 if (!newDataArtifacts.isEmpty()) {
391 ingestJob.addDataArtifacts(newDataArtifacts);
393 if (!newAnalysisResults.isEmpty()) {
394 ingestJob.addAnalysisResults(newAnalysisResults);
403 for (BlackboardArtifact.Type artifactType : tskEvent.getArtifactTypes()) {
406 eventPublishingExecutor.submit(
new PublishEventTask(autopsyEvent, moduleEventPublisher));
419 void handleCaseClosed() {
426 jobEventPublisher.closeRemoteEventChannel();
427 moduleEventPublisher.closeRemoteEventChannel();
429 clearIngestMessageBox();
444 if (!(dataSource instanceof DataSource)) {
445 throw new IllegalArgumentException(
"dataSource argument does not implement the DataSource interface");
448 IngestJobInputStream stream =
new IngestJobInputStream(job);
449 if (stream.getIngestJobStartResult().
getJob() !=
null) {
451 }
else if (stream.getIngestJobStartResult().
getModuleErrors().isEmpty()) {
453 logger.log(Level.SEVERE, String.format(
"%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.getName()), error.getThrowable());
455 throw new TskCoreException(
"Error starting ingest modules");
457 throw new TskCoreException(
"Error starting ingest modules", stream.getIngestJobStartResult().
getStartupException());
479 List<AbstractFile> emptyFilesSubset =
new ArrayList<>();
480 for (Content dataSource : dataSources) {
496 if (!(dataSource instanceof DataSource)) {
497 throw new IllegalArgumentException(
"dataSource argument does not implement the DataSource interface");
501 if (job.hasIngestPipeline()) {
526 List<DataSource> verifiedDataSources =
new ArrayList<>();
527 for (Content content : dataSources) {
528 if (!(content instanceof DataSource)) {
529 throw new IllegalArgumentException(
"Content object in dataSources argument does not implement the DataSource interface");
531 DataSource verifiedDataSource = (DataSource) content;
532 verifiedDataSources.add(verifiedDataSource);
536 for (DataSource dataSource : verifiedDataSources) {
537 List<IngestJob> startedJobs =
new ArrayList<>();
539 if (job.hasIngestPipeline()) {
540 startResult = startIngestJob(job);
542 startedJobs.add(job);
544 for (
IngestJob jobToCancel : startedJobs) {
569 "IngestManager.startupErr.dlgTitle=Ingest Module Startup Failure",
570 "IngestManager.startupErr.dlgMsg=Unable to start up one or more ingest modules, ingest cancelled.",
571 "IngestManager.startupErr.dlgSolution=Please disable the failed modules or fix the errors before restarting ingest.",
572 "IngestManager.startupErr.dlgErrorList=Errors:"
574 IngestJobStartResult startIngestJob(IngestJob job) {
578 if (!GraphicsEnvironment.isHeadless()) {
579 if (SwingUtilities.isEventDispatchThread()) {
580 initIngestMessageInbox();
583 SwingUtilities.invokeAndWait(() -> initIngestMessageInbox());
584 }
catch (InterruptedException ex) {
586 }
catch (InvocationTargetException ex) {
587 logger.log(Level.WARNING,
"There was an error starting ingest message inbox", ex);
592 List<IngestModuleError> errors =
null;
603 EventQueue.invokeLater(
new Runnable() {
607 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
608 NbBundle.getMessage(
this.getClass(),
"IngestManager.cancellingIngest.msgDlg.text"),
609 NbBundle.getMessage(
this.getClass(),
"IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName),
610 JOptionPane.ERROR_MESSAGE);
621 if (!ingestMonitor.isRunning()) {
622 ingestMonitor.start();
625 synchronized (ingestJobsById) {
626 ingestJobsById.put(job.getId(), job);
628 IngestManager.
logger.log(Level.INFO, String.format(
"Starting ingest job %d at %s", job.getId(),
new Date().getTime()));
630 errors = job.start();
631 }
catch (InterruptedException ex) {
632 return new IngestJobStartResult(
null,
new IngestManagerException(
"Interrupted while starting ingest", ex), errors);
634 if (errors.isEmpty()) {
635 this.fireIngestJobStarted(job.getId());
637 synchronized (ingestJobsById) {
638 this.ingestJobsById.remove(job.getId());
641 logger.log(Level.SEVERE, String.format(
"Error starting %s ingest module for job %d", error.getModuleDisplayName(), job.getId()), error.getThrowable());
643 IngestManager.
logger.log(Level.SEVERE,
"Ingest job {0} could not be started", job.getId());
645 final StringBuilder message =
new StringBuilder(1024);
646 message.append(Bundle.IngestManager_startupErr_dlgMsg()).append(
"\n");
647 message.append(Bundle.IngestManager_startupErr_dlgSolution()).append(
"\n\n");
648 message.append(Bundle.IngestManager_startupErr_dlgErrorList()).append(
"\n");
650 String moduleName = error.getModuleDisplayName();
651 String errorMessage = error.getThrowable().getLocalizedMessage();
652 message.append(moduleName).append(
": ").append(errorMessage).append(
"\n");
654 message.append(
"\n\n");
655 EventQueue.invokeLater(() -> {
656 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), message, Bundle.IngestManager_startupErr_dlgTitle(), JOptionPane.ERROR_MESSAGE);
659 return new IngestJobStartResult(
null,
new IngestManagerException(
"Errors occurred while starting ingest"), errors);
671 long jobId = job.getId();
672 synchronized (ingestJobsById) {
673 ingestJobsById.remove(jobId);
675 if (!job.isCancelled()) {
676 IngestManager.
logger.log(Level.INFO, String.format(
"Ingest job %d completed at %s", job.getId(),
new Date().getTime()));
677 fireIngestJobCompleted(jobId);
679 IngestManager.
logger.log(Level.INFO, String.format(
"Ingest job %d cancelled at %s", job.getId(),
new Date().getTime()));
680 fireIngestJobCancelled(jobId);
803 void fireIngestJobStarted(
long ingestJobId) {
805 eventPublishingExecutor.submit(
new PublishEventTask(event, jobEventPublisher));
813 void fireIngestJobCompleted(
long ingestJobId) {
814 AutopsyEvent
event =
new AutopsyEvent(IngestJobEvent.COMPLETED.toString(), ingestJobId,
null);
815 eventPublishingExecutor.submit(
new PublishEventTask(event, jobEventPublisher));
823 void fireIngestJobCancelled(
long ingestJobId) {
824 AutopsyEvent
event =
new AutopsyEvent(IngestJobEvent.CANCELLED.toString(), ingestJobId,
null);
825 eventPublishingExecutor.submit(
new PublishEventTask(event, jobEventPublisher));
835 void fireDataSourceAnalysisStarted(
long ingestJobId, Content dataSource) {
836 AutopsyEvent
event =
new DataSourceAnalysisStartedEvent(ingestJobId, dataSource);
837 eventPublishingExecutor.submit(
new PublishEventTask(event, jobEventPublisher));
847 void fireDataSourceAnalysisCompleted(
long ingestJobId, Content dataSource) {
848 AutopsyEvent
event =
new DataSourceAnalysisCompletedEvent(ingestJobId, dataSource, DataSourceAnalysisCompletedEvent.Reason.ANALYSIS_COMPLETED);
849 eventPublishingExecutor.submit(
new PublishEventTask(event, jobEventPublisher));
859 void fireDataSourceAnalysisCancelled(
long ingestJobId, Content dataSource) {
861 eventPublishingExecutor.submit(
new PublishEventTask(event, jobEventPublisher));
870 void fireFileIngestDone(AbstractFile file) {
872 eventPublishingExecutor.submit(
new PublishEventTask(event, moduleEventPublisher));
884 eventPublishingExecutor.submit(
new PublishEventTask(event, moduleEventPublisher));
895 void initIngestMessageInbox() {
896 synchronized (this.ingestMessageBoxLock) {
897 ingestMessageBox = IngestMessageTopComponent.findInstance();
907 synchronized (this.ingestMessageBoxLock) {
910 ingestMessageBox.displayMessage(message);
912 long errorPosts = ingestErrorMessagePosts.incrementAndGet();
913 if (errorPosts <= MAX_ERROR_MESSAGE_POSTS) {
914 ingestMessageBox.displayMessage(message);
915 }
else if (errorPosts == MAX_ERROR_MESSAGE_POSTS + 1) {
917 NbBundle.getMessage(
this.getClass(),
"IngestManager.IngestMessage.ErrorMessageLimitReached.title"),
918 NbBundle.getMessage(
this.getClass(),
"IngestManager.IngestMessage.ErrorMessageLimitReached.subject"),
919 NbBundle.getMessage(
this.getClass(),
"IngestManager.IngestMessage.ErrorMessageLimitReached.msg", MAX_ERROR_MESSAGE_POSTS));
920 ingestMessageBox.displayMessage(errorMessageLimitReachedMessage);
949 void setIngestTaskProgress(IngestTask task, String currentModuleName) {
950 IngestThreadActivitySnapshot prevSnap = ingestThreadActivitySnapshots.get(task.getThreadId());
951 IngestThreadActivitySnapshot newSnap =
new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJobExecutor().getIngestJobId(), currentModuleName, task.getDataSource(), task.getContentName());
952 ingestThreadActivitySnapshots.put(task.getThreadId(), newSnap);
953 incrementModuleRunTime(prevSnap.getModuleDisplayName(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
963 void setIngestTaskProgressCompleted(IngestTask task) {
964 IngestThreadActivitySnapshot prevSnap = ingestThreadActivitySnapshots.get(task.getThreadId());
965 IngestThreadActivitySnapshot newSnap =
new IngestThreadActivitySnapshot(task.getThreadId());
966 ingestThreadActivitySnapshots.put(task.getThreadId(), newSnap);
967 incrementModuleRunTime(prevSnap.getModuleDisplayName(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
976 void incrementModuleRunTime(String moduleDisplayName, Long duration) {
977 if (moduleDisplayName.equals(
"IDLE")) {
981 synchronized (ingestModuleRunTimes) {
982 Long prevTimeL = ingestModuleRunTimes.get(moduleDisplayName);
984 if (prevTimeL !=
null) {
985 prevTime = prevTimeL;
987 prevTime += duration;
988 ingestModuleRunTimes.put(moduleDisplayName, prevTime);
1023 List<IngestJobProgressSnapshot> snapShots =
new ArrayList<>();
1027 if (snapshot !=
null) {
1028 snapShots.add(snapshot);
1041 long getFreeDiskSpace() {
1042 if (ingestMonitor !=
null) {
1043 return ingestMonitor.getFreeSpace();
1052 private final class StartIngestJobTask
implements Callable<Void> {
1066 if (Thread.currentThread().isInterrupted()) {
1074 final String displayName = NbBundle.getMessage(this.getClass(),
"IngestManager.StartIngestJobsTask.run.displayName");
1075 this.progress = ProgressHandle.createHandle(displayName,
new Cancellable() {
1077 public boolean cancel() {
1079 progress.setDisplayName(NbBundle.getMessage(
this.getClass(),
"IngestManager.StartIngestJobsTask.run.cancelling", displayName));
1083 handle.cancel(
true);
1091 startIngestJob(
job);
1109 private final class ExecuteIngestJobTasksTask
implements Runnable {
1123 IngestTask task =
tasks.getNextTask();
1125 }
catch (InterruptedException ex) {
1128 if (Thread.currentThread().isInterrupted()) {
1138 private static final class PublishEventTask
implements Runnable {
1166 public static final class IngestThreadActivitySnapshot
implements Serializable {
1182 IngestThreadActivitySnapshot(
long threadId) {
1185 this.moduleDisplayName = NbBundle.getMessage(this.getClass(),
"IngestManager.IngestThreadActivitySnapshot.idleThread");
1186 this.dataSourceName =
"";
1205 this.threadId = threadId;
1207 startTime =
new Date();
1208 this.moduleDisplayName = moduleDisplayName;
1209 this.dataSourceName = dataSource.getName();
1228 IngestThreadActivitySnapshot(
long threadId,
long jobId, String moduleDisplayName, Content dataSource, String fileName) {
1229 this.threadId = threadId;
1231 startTime =
new Date();
1232 this.moduleDisplayName = moduleDisplayName;
1233 this.dataSourceName = dataSource.getName();
1234 this.fileName = fileName;
1243 long getIngestJobId() {
1252 long getThreadId() {
1261 Date getStartTime() {
1262 return new Date(startTime.getTime());
1270 String getModuleDisplayName() {
1271 return moduleDisplayName;
1280 String getDataSourceName() {
1281 return dataSourceName;
1289 String getFileName() {
1389 super(message, cause);
1403 instance.addIngestJobEventListener(listener);
1404 instance.addIngestModuleEventListener(listener);
1417 instance.removeIngestJobEventListener(listener);
1418 instance.removeIngestModuleEventListener(listener);