Autopsy  4.6.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
IngestManager.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2012-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.ingest;
20 
21 import com.google.common.util.concurrent.ThreadFactoryBuilder;
22 import java.awt.EventQueue;
23 import java.beans.PropertyChangeEvent;
24 import java.beans.PropertyChangeListener;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Date;
29 import java.util.EnumSet;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.concurrent.Callable;
36 import java.util.concurrent.ConcurrentHashMap;
37 import java.util.concurrent.ExecutorService;
38 import java.util.concurrent.Executors;
39 import java.util.concurrent.Future;
40 import java.util.concurrent.atomic.AtomicLong;
41 import java.util.logging.Level;
42 import java.util.stream.Collectors;
43 import java.util.stream.Stream;
44 import javax.annotation.concurrent.GuardedBy;
45 import javax.annotation.concurrent.Immutable;
46 import javax.annotation.concurrent.ThreadSafe;
47 import javax.swing.JOptionPane;
48 import org.netbeans.api.progress.ProgressHandle;
49 import org.openide.util.Cancellable;
50 import org.openide.util.NbBundle;
51 import org.openide.windows.WindowManager;
67 import org.sleuthkit.datamodel.AbstractFile;
68 import org.sleuthkit.datamodel.Content;
69 
109 @ThreadSafe
110 public class IngestManager {
111 
112  private final static Logger logger = Logger.getLogger(IngestManager.class.getName());
113  private final static String INGEST_JOB_EVENT_CHANNEL_NAME = "%s-Ingest-Job-Events"; //NON-NLS
114  private final static Set<String> INGEST_JOB_EVENT_NAMES = Stream.of(IngestJobEvent.values()).map(IngestJobEvent::toString).collect(Collectors.toSet());
115  private final static String INGEST_MODULE_EVENT_CHANNEL_NAME = "%s-Ingest-Module-Events"; //NON-NLS
116  private final static Set<String> INGEST_MODULE_EVENT_NAMES = Stream.of(IngestModuleEvent.values()).map(IngestModuleEvent::toString).collect(Collectors.toSet());
117  private final static int MAX_ERROR_MESSAGE_POSTS = 200;
118  @GuardedBy("IngestManager.class")
119  private static IngestManager instance;
120  private final int numberOfFileIngestThreads;
121  private final AtomicLong nextIngestManagerTaskId = new AtomicLong(0L);
122  private final ExecutorService startIngestJobsExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-start-ingest-jobs-%d").build()); //NON-NLS;
123  private final Map<Long, Future<Void>> startIngestJobFutures = new ConcurrentHashMap<>();
124  private final Map<Long, IngestJob> ingestJobsById = new HashMap<>();
125  private final ExecutorService dataSourceLevelIngestJobTasksExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-data-source-ingest-%d").build()); //NON-NLS;
126  private final ExecutorService fileLevelIngestJobTasksExecutor;
127  private final ExecutorService eventPublishingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-ingest-events-%d").build()); //NON-NLS;
131  private final AutopsyEventPublisher moduleEventPublisher = new AutopsyEventPublisher();
132  private final Object ingestMessageBoxLock = new Object();
133  private final AtomicLong ingestErrorMessagePosts = new AtomicLong(0L);
134  private final ConcurrentHashMap<Long, IngestThreadActivitySnapshot> ingestThreadActivitySnapshots = new ConcurrentHashMap<>();
135  private final ConcurrentHashMap<String, Long> ingestModuleRunTimes = new ConcurrentHashMap<>();
136  private volatile IngestMessageTopComponent ingestMessageBox;
137  private volatile boolean caseIsOpen;
138 
145  public synchronized static IngestManager getInstance() {
146  if (null == instance) {
147  instance = new IngestManager();
148  instance.subscribeToServiceMonitorEvents();
149  instance.subscribeToCaseEvents();
150  }
151  return instance;
152  }
153 
158  private IngestManager() {
159  /*
160  * Submit a single Runnable ingest manager task for processing data
161  * source level ingest job tasks to the data source level ingest job
162  * tasks executor.
163  */
164  long threadId = nextIngestManagerTaskId.incrementAndGet();
165  dataSourceLevelIngestJobTasksExecutor.submit(new ExecuteIngestJobTasksTask(threadId, IngestTasksScheduler.getInstance().getDataSourceIngestTaskQueue()));
166  ingestThreadActivitySnapshots.put(threadId, new IngestThreadActivitySnapshot(threadId));
167 
168  /*
169  * Submit a configurable number of Runnable ingest manager tasks for
170  * processing file level ingest job tasks to the file level ingest job
171  * tasks executor.
172  */
174  fileLevelIngestJobTasksExecutor = Executors.newFixedThreadPool(numberOfFileIngestThreads, new ThreadFactoryBuilder().setNameFormat("IM-file-ingest-%d").build()); //NON-NLS
175  for (int i = 0; i < numberOfFileIngestThreads; ++i) {
176  threadId = nextIngestManagerTaskId.incrementAndGet();
177  fileLevelIngestJobTasksExecutor.submit(new ExecuteIngestJobTasksTask(threadId, IngestTasksScheduler.getInstance().getFileIngestTaskQueue()));
178  ingestThreadActivitySnapshots.put(threadId, new IngestThreadActivitySnapshot(threadId));
179  }
180  }
181 
187  PropertyChangeListener propChangeListener = (PropertyChangeEvent evt) -> {
188  if (evt.getNewValue().equals(ServicesMonitor.ServiceStatus.DOWN.toString())) {
189  /*
190  * The application services considered to be key services are
191  * only necessary for multi-user cases.
192  */
193  try {
195  return;
196  }
197  } catch (NoCurrentCaseException noCaseOpenException) {
198  return;
199  }
200 
201  String serviceDisplayName = ServicesMonitor.Service.valueOf(evt.getPropertyName()).getDisplayName();
202  logger.log(Level.SEVERE, "Service {0} is down, cancelling all running ingest jobs", serviceDisplayName); //NON-NLS
204  EventQueue.invokeLater(new Runnable() {
205  @Override
206  public void run() {
207  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
208  NbBundle.getMessage(this.getClass(), "IngestManager.cancellingIngest.msgDlg.text"),
209  NbBundle.getMessage(this.getClass(), "IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName),
210  JOptionPane.ERROR_MESSAGE);
211  }
212  });
213  }
215  }
216  };
217 
218  /*
219  * The key services for multi-user cases are currently the case database
220  * server and the Solr server. The Solr server is a key service not
221  * because search is essential, but because the coordination service
222  * (ZooKeeper) is running embedded within the Solr server.
223  */
224  Set<String> servicesList = new HashSet<>();
225  servicesList.add(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString());
226  servicesList.add(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString());
227  this.servicesMonitor.addSubscriber(servicesList, propChangeListener);
228  }
229 
234  private void subscribeToCaseEvents() {
235  Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent event) -> {
236  if (event.getNewValue() != null) {
237  handleCaseOpened();
238  } else {
239  handleCaseClosed();
240  }
241  });
242  }
243 
244  /*
245  * Handles a current case opened event by clearing the ingest messages inbox
246  * and opening a remote event channel for the current case.
247  *
248  * Note that current case change events are published in a strictly
249  * serialized manner, i.e., one event at a time, synchronously.
250  */
251  void handleCaseOpened() {
252  caseIsOpen = true;
254  try {
255  Case openedCase = Case.getOpenCase();
256  String channelPrefix = openedCase.getName();
257  if (Case.CaseType.MULTI_USER_CASE == openedCase.getCaseType()) {
258  jobEventPublisher.openRemoteEventChannel(String.format(INGEST_JOB_EVENT_CHANNEL_NAME, channelPrefix));
259  moduleEventPublisher.openRemoteEventChannel(String.format(INGEST_MODULE_EVENT_CHANNEL_NAME, channelPrefix));
260  }
261  } catch (NoCurrentCaseException | AutopsyEventException ex) {
262  logger.log(Level.SEVERE, "Failed to open remote events channel", ex); //NON-NLS
263  MessageNotifyUtil.Notify.error(NbBundle.getMessage(IngestManager.class, "IngestManager.OpenEventChannel.Fail.Title"),
264  NbBundle.getMessage(IngestManager.class, "IngestManager.OpenEventChannel.Fail.ErrMsg"));
265  }
266  }
267 
268  /*
269  * Handles a current case closed event by cancelling all ingest jobs for the
270  * case, closing the remote event channel for the case, and clearing the
271  * ingest messages inbox.
272  *
273  * Note that current case change events are published in a strictly
274  * serialized manner, i.e., one event at a time, synchronously.
275  */
276  void handleCaseClosed() {
277  /*
278  * TODO (JIRA-2227): IngestManager should wait for cancelled ingest jobs
279  * to complete when a case is closed.
280  */
281  this.cancelAllIngestJobs(IngestJob.CancellationReason.CASE_CLOSED);
284  caseIsOpen = false;
286  }
287 
296  }
297 
304  public void queueIngestJob(Collection<Content> dataSources, IngestJobSettings settings) {
305  if (caseIsOpen) {
306  IngestJob job = new IngestJob(dataSources, settings, RuntimeProperties.runningWithGUI());
307  if (job.hasIngestPipeline()) {
308  long taskId = nextIngestManagerTaskId.incrementAndGet();
309  Future<Void> task = startIngestJobsExecutor.submit(new StartIngestJobTask(taskId, job));
310  startIngestJobFutures.put(taskId, task);
311  }
312  }
313  }
314 
323  public void queueIngestJob(Content dataSource, List<AbstractFile> files, IngestJobSettings settings) {
324  if (caseIsOpen) {
325  IngestJob job = new IngestJob(dataSource, files, settings, RuntimeProperties.runningWithGUI());
326  if (job.hasIngestPipeline()) {
327  long taskId = nextIngestManagerTaskId.incrementAndGet();
328  Future<Void> task = startIngestJobsExecutor.submit(new StartIngestJobTask(taskId, job));
329  startIngestJobFutures.put(taskId, task);
330  }
331  }
332  }
333 
343  public IngestJobStartResult beginIngestJob(Collection<Content> dataSources, IngestJobSettings settings) {
344  if (caseIsOpen) {
345  IngestJob job = new IngestJob(dataSources, settings, RuntimeProperties.runningWithGUI());
346  if (job.hasIngestPipeline()) {
347  return startIngestJob(job);
348  }
349  return new IngestJobStartResult(null, new IngestManagerException("No ingest pipeline created, likely due to no ingest modules being enabled"), null); //NON-NLS
350  }
351  return new IngestJobStartResult(null, new IngestManagerException("No case open"), null); //NON-NLS
352  }
353 
362  @NbBundle.Messages({
363  "IngestManager.startupErr.dlgTitle=Ingest Module Startup Failure",
364  "IngestManager.startupErr.dlgMsg=Unable to start up one or more ingest modules, ingest cancelled.",
365  "IngestManager.startupErr.dlgSolution=Please disable the failed modules or fix the errors before restarting ingest.",
366  "IngestManager.startupErr.dlgErrorList=Errors:"
367  })
369  List<IngestModuleError> errors = null;
370  Case openCase;
371  try {
372  openCase = Case.getOpenCase();
373  } catch (NoCurrentCaseException ex) {
374  return new IngestJobStartResult(null, new IngestManagerException("Exception while getting open case.", ex), Collections.<IngestModuleError>emptyList()); //NON-NLS
375  }
376  if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) {
377  try {
380  EventQueue.invokeLater(new Runnable() {
381  @Override
382  public void run() {
383  String serviceDisplayName = ServicesMonitor.Service.REMOTE_CASE_DATABASE.getDisplayName();
384  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
385  NbBundle.getMessage(this.getClass(), "IngestManager.cancellingIngest.msgDlg.text"),
386  NbBundle.getMessage(this.getClass(), "IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName),
387  JOptionPane.ERROR_MESSAGE);
388  }
389  });
390  }
391  return new IngestJobStartResult(null, new IngestManagerException("Ingest aborted. Remote database is down"), Collections.<IngestModuleError>emptyList()); //NON-NLS
392  }
394  return new IngestJobStartResult(null, new IngestManagerException("Database server is down", ex), Collections.<IngestModuleError>emptyList()); //NON-NLS
395  }
396  }
397 
398  if (!ingestMonitor.isRunning()) {
399  ingestMonitor.start();
400  }
401 
402  synchronized (ingestJobsById) {
403  ingestJobsById.put(job.getId(), job);
404  }
405  errors = job.start();
406  if (errors.isEmpty()) {
407  this.fireIngestJobStarted(job.getId());
408  IngestManager.logger.log(Level.INFO, "Ingest job {0} started", job.getId()); //NON-NLS
409  } else {
410  synchronized (ingestJobsById) {
411  this.ingestJobsById.remove(job.getId());
412  }
413  for (IngestModuleError error : errors) {
414  logger.log(Level.SEVERE, String.format("Error starting %s ingest module for job %d", error.getModuleDisplayName(), job.getId()), error.getThrowable()); //NON-NLS
415  }
416  IngestManager.logger.log(Level.SEVERE, "Ingest job {0} could not be started", job.getId()); //NON-NLS
418  final StringBuilder message = new StringBuilder(1024);
419  message.append(Bundle.IngestManager_startupErr_dlgMsg()).append("\n"); //NON-NLS
420  message.append(Bundle.IngestManager_startupErr_dlgSolution()).append("\n\n"); //NON-NLS
421  message.append(Bundle.IngestManager_startupErr_dlgErrorList()).append("\n"); //NON-NLS
422  for (IngestModuleError error : errors) {
423  String moduleName = error.getModuleDisplayName();
424  String errorMessage = error.getThrowable().getLocalizedMessage();
425  message.append(moduleName).append(": ").append(errorMessage).append("\n"); //NON-NLS
426  }
427  message.append("\n\n");
428  EventQueue.invokeLater(() -> {
429  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), message, Bundle.IngestManager_startupErr_dlgTitle(), JOptionPane.ERROR_MESSAGE);
430  });
431  }
432  return new IngestJobStartResult(null, new IngestManagerException("Errors occurred while starting ingest"), errors); //NON-NLS
433  }
434 
435  return new IngestJobStartResult(job, null, errors);
436  }
437 
443  void finishIngestJob(IngestJob job) {
444  long jobId = job.getId();
445  synchronized (ingestJobsById) {
446  ingestJobsById.remove(jobId);
447  }
448  if (!job.isCancelled()) {
449  IngestManager.logger.log(Level.INFO, "Ingest job {0} completed", jobId); //NON-NLS
450  fireIngestJobCompleted(jobId);
451  } else {
452  IngestManager.logger.log(Level.INFO, "Ingest job {0} cancelled", jobId); //NON-NLS
453  fireIngestJobCancelled(jobId);
454  }
455  }
456 
463  public boolean isIngestRunning() {
464  synchronized (ingestJobsById) {
465  return !ingestJobsById.isEmpty();
466  }
467  }
468 
475  startIngestJobFutures.values().forEach((handle) -> {
476  handle.cancel(true);
477  });
478  synchronized (ingestJobsById) {
479  this.ingestJobsById.values().forEach((job) -> {
480  job.cancel(reason);
481  });
482  }
483  }
484 
490  public void addIngestJobEventListener(final PropertyChangeListener listener) {
491  jobEventPublisher.addSubscriber(INGEST_JOB_EVENT_NAMES, listener);
492  }
493 
499  public void removeIngestJobEventListener(final PropertyChangeListener listener) {
500  jobEventPublisher.removeSubscriber(INGEST_JOB_EVENT_NAMES, listener);
501  }
502 
508  public void addIngestModuleEventListener(final PropertyChangeListener listener) {
509  moduleEventPublisher.addSubscriber(INGEST_MODULE_EVENT_NAMES, listener);
510  }
511 
517  public void removeIngestModuleEventListener(final PropertyChangeListener listener) {
518  moduleEventPublisher.removeSubscriber(INGEST_MODULE_EVENT_NAMES, listener);
519  }
520 
526  void fireIngestJobStarted(long ingestJobId) {
527  AutopsyEvent event = new AutopsyEvent(IngestJobEvent.STARTED.toString(), ingestJobId, null);
528  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
529  }
530 
536  void fireIngestJobCompleted(long ingestJobId) {
537  AutopsyEvent event = new AutopsyEvent(IngestJobEvent.COMPLETED.toString(), ingestJobId, null);
538  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
539  }
540 
546  void fireIngestJobCancelled(long ingestJobId) {
547  AutopsyEvent event = new AutopsyEvent(IngestJobEvent.CANCELLED.toString(), ingestJobId, null);
548  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
549  }
550 
559  void fireDataSourceAnalysisStarted(long ingestJobId, long dataSourceIngestJobId, Content dataSource) {
560  AutopsyEvent event = new DataSourceAnalysisStartedEvent(ingestJobId, dataSourceIngestJobId, dataSource);
561  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
562  }
563 
572  void fireDataSourceAnalysisCompleted(long ingestJobId, long dataSourceIngestJobId, Content dataSource) {
573  AutopsyEvent event = new DataSourceAnalysisCompletedEvent(ingestJobId, dataSourceIngestJobId, dataSource, DataSourceAnalysisCompletedEvent.Reason.ANALYSIS_COMPLETED);
574  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
575  }
576 
585  void fireDataSourceAnalysisCancelled(long ingestJobId, long dataSourceIngestJobId, Content dataSource) {
586  AutopsyEvent event = new DataSourceAnalysisCompletedEvent(ingestJobId, dataSourceIngestJobId, dataSource, DataSourceAnalysisCompletedEvent.Reason.ANALYSIS_CANCELLED);
587  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
588  }
589 
596  void fireFileIngestDone(AbstractFile file) {
597  AutopsyEvent event = new FileAnalyzedEvent(file);
598  eventPublishingExecutor.submit(new PublishEventTask(event, moduleEventPublisher));
599  }
600 
608  void fireIngestModuleDataEvent(ModuleDataEvent moduleDataEvent) {
609  AutopsyEvent event = new BlackboardPostEvent(moduleDataEvent);
610  eventPublishingExecutor.submit(new PublishEventTask(event, moduleEventPublisher));
611  }
612 
620  void fireIngestModuleContentEvent(ModuleContentEvent moduleContentEvent) {
621  AutopsyEvent event = new ContentChangedEvent(moduleContentEvent);
622  eventPublishingExecutor.submit(new PublishEventTask(event, moduleEventPublisher));
623  }
624 
630  void initIngestMessageInbox() {
631  synchronized (this.ingestMessageBoxLock) {
632  ingestMessageBox = IngestMessageTopComponent.findInstance();
633  }
634  }
635 
641  void postIngestMessage(IngestMessage message) {
642  synchronized (this.ingestMessageBoxLock) {
643  if (ingestMessageBox != null && RuntimeProperties.runningWithGUI()) {
644  if (message.getMessageType() != IngestMessage.MessageType.ERROR && message.getMessageType() != IngestMessage.MessageType.WARNING) {
645  ingestMessageBox.displayMessage(message);
646  } else {
647  long errorPosts = ingestErrorMessagePosts.incrementAndGet();
648  if (errorPosts <= MAX_ERROR_MESSAGE_POSTS) {
649  ingestMessageBox.displayMessage(message);
650  } else if (errorPosts == MAX_ERROR_MESSAGE_POSTS + 1) {
651  IngestMessage errorMessageLimitReachedMessage = IngestMessage.createErrorMessage(
652  NbBundle.getMessage(this.getClass(), "IngestManager.IngestMessage.ErrorMessageLimitReached.title"),
653  NbBundle.getMessage(this.getClass(), "IngestManager.IngestMessage.ErrorMessageLimitReached.subject"),
654  NbBundle.getMessage(this.getClass(), "IngestManager.IngestMessage.ErrorMessageLimitReached.msg", MAX_ERROR_MESSAGE_POSTS));
655  ingestMessageBox.displayMessage(errorMessageLimitReachedMessage);
656  }
657  }
658  }
659  }
660  }
661 
662  /*
663  * Clears the ingest messages inbox.
664  */
665  private void clearIngestMessageBox() {
666  synchronized (this.ingestMessageBoxLock) {
667  if (null != ingestMessageBox) {
668  ingestMessageBox.clearMessages();
669  }
671  }
672  }
673 
685  void setIngestTaskProgress(DataSourceIngestTask task, String ingestModuleDisplayName) {
686  ingestThreadActivitySnapshots.put(task.getThreadId(), new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJob().getId(), ingestModuleDisplayName, task.getDataSource()));
687  }
688 
700  void setIngestTaskProgress(FileIngestTask task, String ingestModuleDisplayName) {
701  IngestThreadActivitySnapshot prevSnap = ingestThreadActivitySnapshots.get(task.getThreadId());
702  IngestThreadActivitySnapshot newSnap = new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJob().getId(), ingestModuleDisplayName, task.getDataSource(), task.getFile());
703  ingestThreadActivitySnapshots.put(task.getThreadId(), newSnap);
704  incrementModuleRunTime(prevSnap.getActivity(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
705  }
706 
714  void setIngestTaskProgressCompleted(DataSourceIngestTask task) {
715  ingestThreadActivitySnapshots.put(task.getThreadId(), new IngestThreadActivitySnapshot(task.getThreadId()));
716  }
717 
725  void setIngestTaskProgressCompleted(FileIngestTask task) {
726  IngestThreadActivitySnapshot prevSnap = ingestThreadActivitySnapshots.get(task.getThreadId());
727  IngestThreadActivitySnapshot newSnap = new IngestThreadActivitySnapshot(task.getThreadId());
728  ingestThreadActivitySnapshots.put(task.getThreadId(), newSnap);
729  incrementModuleRunTime(prevSnap.getActivity(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
730  }
731 
738  private void incrementModuleRunTime(String moduleDisplayName, Long duration) {
739  if (moduleDisplayName.equals("IDLE")) { //NON-NLS
740  return;
741  }
742 
743  synchronized (ingestModuleRunTimes) {
744  Long prevTimeL = ingestModuleRunTimes.get(moduleDisplayName);
745  long prevTime = 0;
746  if (prevTimeL != null) {
747  prevTime = prevTimeL;
748  }
749  prevTime += duration;
750  ingestModuleRunTimes.put(moduleDisplayName, prevTime);
751  }
752  }
753 
759  Map<String, Long> getModuleRunTimes() {
760  synchronized (ingestModuleRunTimes) {
761  Map<String, Long> times = new HashMap<>(ingestModuleRunTimes);
762  return times;
763  }
764  }
765 
772  List<IngestThreadActivitySnapshot> getIngestThreadActivitySnapshots() {
773  return new ArrayList<>(ingestThreadActivitySnapshots.values());
774  }
775 
781  List<DataSourceIngestJob.Snapshot> getIngestJobSnapshots() {
782  List<DataSourceIngestJob.Snapshot> snapShots = new ArrayList<>();
783  synchronized (ingestJobsById) {
784  ingestJobsById.values().forEach((job) -> {
785  snapShots.addAll(job.getDataSourceIngestJobSnapshots());
786  });
787  }
788  return snapShots;
789  }
790 
797  long getFreeDiskSpace() {
798  if (ingestMonitor != null) {
799  return ingestMonitor.getFreeSpace();
800  } else {
801  return -1;
802  }
803  }
804 
808  private final class StartIngestJobTask implements Callable<Void> {
809 
810  private final long threadId;
811  private final IngestJob job;
812  private ProgressHandle progress;
813 
814  StartIngestJobTask(long threadId, IngestJob job) {
815  this.threadId = threadId;
816  this.job = job;
817  }
818 
819  @Override
820  public Void call() {
821  try {
822  if (Thread.currentThread().isInterrupted()) {
823  synchronized (ingestJobsById) {
824  ingestJobsById.remove(job.getId());
825  }
826  return null;
827  }
828 
830  final String displayName = NbBundle.getMessage(this.getClass(), "IngestManager.StartIngestJobsTask.run.displayName");
831  this.progress = ProgressHandle.createHandle(displayName, new Cancellable() {
832  @Override
833  public boolean cancel() {
834  if (progress != null) {
835  progress.setDisplayName(NbBundle.getMessage(this.getClass(), "IngestManager.StartIngestJobsTask.run.cancelling", displayName));
836  }
837  Future<?> handle = startIngestJobFutures.remove(threadId);
838  handle.cancel(true);
839  return true;
840  }
841  });
842  progress.start();
843  }
844 
845  startIngestJob(job);
846  return null;
847 
848  } finally {
849  if (null != progress) {
850  progress.finish();
851  }
852  startIngestJobFutures.remove(threadId);
853  }
854  }
855 
856  }
857 
861  private final class ExecuteIngestJobTasksTask implements Runnable {
862 
863  private final long threadId;
864  private final BlockingIngestTaskQueue tasks;
865 
866  ExecuteIngestJobTasksTask(long threadId, BlockingIngestTaskQueue tasks) {
867  this.threadId = threadId;
868  this.tasks = tasks;
869  }
870 
871  @Override
872  public void run() {
873  while (true) {
874  try {
875  IngestTask task = tasks.getNextTask(); // Blocks.
876  task.execute(threadId);
877  } catch (InterruptedException ex) {
878  break;
879  }
880  if (Thread.currentThread().isInterrupted()) {
881  break;
882  }
883  }
884  }
885  }
886 
890  private static final class PublishEventTask implements Runnable {
891 
892  private final AutopsyEvent event;
894 
903  this.event = event;
904  this.publisher = publisher;
905  }
906 
907  @Override
908  public void run() {
909  publisher.publish(event);
910  }
911 
912  }
913 
918  @Immutable
919  static final class IngestThreadActivitySnapshot {
920 
921  private final long threadId;
922  private final Date startTime;
923  private final String activity;
924  private final String dataSourceName;
925  private final String fileName;
926  private final long jobId;
927 
935  IngestThreadActivitySnapshot(long threadId) {
936  this.threadId = threadId;
937  startTime = new Date();
938  this.activity = NbBundle.getMessage(this.getClass(), "IngestManager.IngestThreadActivitySnapshot.idleThread");
939  this.dataSourceName = "";
940  this.fileName = "";
941  this.jobId = 0;
942  }
943 
954  IngestThreadActivitySnapshot(long threadId, long jobId, String activity, Content dataSource) {
955  this.threadId = threadId;
956  this.jobId = jobId;
957  startTime = new Date();
958  this.activity = activity;
959  this.dataSourceName = dataSource.getName();
960  this.fileName = "";
961  }
962 
975  IngestThreadActivitySnapshot(long threadId, long jobId, String activity, Content dataSource, AbstractFile file) {
976  this.threadId = threadId;
977  this.jobId = jobId;
978  startTime = new Date();
979  this.activity = activity;
980  this.dataSourceName = dataSource.getName();
981  this.fileName = file.getName();
982  }
983 
989  long getIngestJobId() {
990  return jobId;
991  }
992 
998  long getThreadId() {
999  return threadId;
1000  }
1001 
1007  Date getStartTime() {
1008  return startTime;
1009  }
1010 
1016  String getActivity() {
1017  return activity;
1018  }
1019 
1027  String getDataSourceName() {
1028  return dataSourceName;
1029  }
1030 
1036  String getFileName() {
1037  return fileName;
1038  }
1039 
1040  }
1041 
1045  public enum IngestJobEvent {
1046 
1083  }
1084 
1088  public enum IngestModuleEvent {
1089 
1112  }
1113 
1117  public final static class IngestManagerException extends Exception {
1118 
1119  private static final long serialVersionUID = 1L;
1120 
1126  private IngestManagerException(String message) {
1127  super(message);
1128  }
1129 
1136  private IngestManagerException(String message, Throwable cause) {
1137  super(message, cause);
1138  }
1139  }
1140 
1149  @Deprecated
1150  public static void addPropertyChangeListener(final PropertyChangeListener listener) {
1153  }
1154 
1163  @Deprecated
1164  public static void removePropertyChangeListener(final PropertyChangeListener listener) {
1167  }
1168 
1179  @Deprecated
1180  public synchronized IngestJob startIngestJob(Collection<Content> dataSources, IngestJobSettings settings) {
1181  return beginIngestJob(dataSources, settings).getJob();
1182  }
1183 
1190  @Deprecated
1191  public void cancelAllIngestJobs() {
1193  }
1194 
1195 }
final ConcurrentHashMap< String, Long > ingestModuleRunTimes
final Map< Long, Future< Void > > startIngestJobFutures
void removeIngestModuleEventListener(final PropertyChangeListener listener)
void queueIngestJob(Content dataSource, List< AbstractFile > files, IngestJobSettings settings)
static synchronized IngestManager getInstance()
IngestJobStartResult startIngestJob(IngestJob job)
final ExecutorService dataSourceLevelIngestJobTasksExecutor
static void addPropertyChangeListener(final PropertyChangeListener listener)
IngestJobStartResult beginIngestJob(Collection< Content > dataSources, IngestJobSettings settings)
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
static void removePropertyChangeListener(final PropertyChangeListener listener)
static final Set< String > INGEST_MODULE_EVENT_NAMES
volatile IngestMessageTopComponent ingestMessageBox
void addSubscriber(PropertyChangeListener subscriber)
void removeIngestJobEventListener(final PropertyChangeListener listener)
static final Set< String > INGEST_JOB_EVENT_NAMES
final AutopsyEventPublisher moduleEventPublisher
synchronized void openRemoteEventChannel(String channelName)
void addIngestJobEventListener(final PropertyChangeListener listener)
void queueIngestJob(Collection< Content > dataSources, IngestJobSettings settings)
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void incrementModuleRunTime(String moduleDisplayName, Long duration)
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:420
synchronized IngestJob startIngestJob(Collection< Content > dataSources, IngestJobSettings settings)
final ConcurrentHashMap< Long, IngestThreadActivitySnapshot > ingestThreadActivitySnapshots
void cancelAllIngestJobs(IngestJob.CancellationReason reason)
final ExecutorService fileLevelIngestJobTasksExecutor
final Map< Long, IngestJob > ingestJobsById
final AutopsyEventPublisher jobEventPublisher

Copyright © 2012-2016 Basis Technology. Generated on: Mon May 7 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.