Autopsy  4.4
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 2011-2017 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.HashMap;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.concurrent.Callable;
35 import java.util.concurrent.ConcurrentHashMap;
36 import java.util.concurrent.ExecutorService;
37 import java.util.concurrent.Executors;
38 import java.util.concurrent.Future;
39 import java.util.concurrent.atomic.AtomicLong;
40 import java.util.logging.Level;
41 import java.util.stream.Collectors;
42 import java.util.stream.Stream;
43 import javax.annotation.concurrent.GuardedBy;
44 import javax.annotation.concurrent.Immutable;
45 import javax.annotation.concurrent.ThreadSafe;
46 import javax.swing.JOptionPane;
47 import org.netbeans.api.progress.ProgressHandle;
48 import org.openide.util.Cancellable;
49 import org.openide.util.NbBundle;
64 import org.sleuthkit.datamodel.AbstractFile;
65 import org.sleuthkit.datamodel.Content;
66 
106 @ThreadSafe
107 public class IngestManager {
108 
109  private final static Logger LOGGER = Logger.getLogger(IngestManager.class.getName());
110  private final static String INGEST_JOB_EVENT_CHANNEL_NAME = "%s-Ingest-Job-Events"; //NON-NLS
111  private final static Set<String> INGEST_JOB_EVENT_NAMES = Stream.of(IngestJobEvent.values()).map(IngestJobEvent::toString).collect(Collectors.toSet());
112  private final static String INGEST_MODULE_EVENT_CHANNEL_NAME = "%s-Ingest-Module-Events"; //NON-NLS
113  private final static Set<String> INGEST_MODULE_EVENT_NAMES = Stream.of(IngestModuleEvent.values()).map(IngestModuleEvent::toString).collect(Collectors.toSet());
114  private final static int MAX_ERROR_MESSAGE_POSTS = 200;
115  @GuardedBy("IngestManager.class")
116  private static IngestManager instance;
117  private final int numberOfFileIngestThreads;
118  private final AtomicLong nextIngestManagerTaskId = new AtomicLong(0L);
119  private final ExecutorService startIngestJobsExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-start-ingest-jobs-%d").build()); //NON-NLS;
120  private final Map<Long, Future<Void>> startIngestJobFutures = new ConcurrentHashMap<>();
121  private final Map<Long, IngestJob> ingestJobsById = new ConcurrentHashMap<>();
122  private final ExecutorService dataSourceLevelIngestJobTasksExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-data-source-ingest-%d").build()); //NON-NLS;
123  private final ExecutorService fileLevelIngestJobTasksExecutor;
124  private final ExecutorService eventPublishingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-ingest-events-%d").build()); //NON-NLS;
128  private final AutopsyEventPublisher moduleEventPublisher = new AutopsyEventPublisher();
129  private final Object ingestMessageBoxLock = new Object();
130  private final AtomicLong ingestErrorMessagePosts = new AtomicLong(0L);
131  private final ConcurrentHashMap<Long, IngestThreadActivitySnapshot> ingestThreadActivitySnapshots = new ConcurrentHashMap<>();
132  private final ConcurrentHashMap<String, Long> ingestModuleRunTimes = new ConcurrentHashMap<>();
133  private volatile IngestMessageTopComponent ingestMessageBox;
134  private volatile boolean caseIsOpen;
135 
142  public synchronized static IngestManager getInstance() {
143  if (null == instance) {
144  instance = new IngestManager();
145  instance.subscribeToServiceMonitorEvents();
146  instance.subscribeToCaseEvents();
147  }
148  return instance;
149  }
150 
155  private IngestManager() {
156  /*
157  * Submit a single Runnable ingest manager task for processing data
158  * source level ingest job tasks to the data source level ingest job
159  * tasks executor.
160  */
161  long threadId = nextIngestManagerTaskId.incrementAndGet();
162  dataSourceLevelIngestJobTasksExecutor.submit(new ExecuteIngestJobTasksTask(threadId, IngestTasksScheduler.getInstance().getDataSourceIngestTaskQueue()));
163  ingestThreadActivitySnapshots.put(threadId, new IngestThreadActivitySnapshot(threadId));
164 
165  /*
166  * Submit a configurable number of Runnable ingest manager tasks for
167  * processing file level ingest job tasks to the file level ingest job
168  * tasks executor.
169  */
171  fileLevelIngestJobTasksExecutor = Executors.newFixedThreadPool(numberOfFileIngestThreads, new ThreadFactoryBuilder().setNameFormat("IM-file-ingest-%d").build()); //NON-NLS
172  for (int i = 0; i < numberOfFileIngestThreads; ++i) {
173  threadId = nextIngestManagerTaskId.incrementAndGet();
174  fileLevelIngestJobTasksExecutor.submit(new ExecuteIngestJobTasksTask(threadId, IngestTasksScheduler.getInstance().getFileIngestTaskQueue()));
175  ingestThreadActivitySnapshots.put(threadId, new IngestThreadActivitySnapshot(threadId));
176  }
177  }
178 
184  PropertyChangeListener propChangeListener = (PropertyChangeEvent evt) -> {
185  if (evt.getNewValue().equals(ServicesMonitor.ServiceStatus.DOWN.toString())) {
186  /*
187  * The application services considered to be key services are
188  * only necessary for multi-user cases.
189  */
190  try {
192  return;
193  }
194  } catch (IllegalStateException noCaseOpenException) {
195  return;
196  }
197 
198  String serviceDisplayName = ServicesMonitor.Service.valueOf(evt.getPropertyName()).getDisplayName();
199  LOGGER.log(Level.SEVERE, "Service {0} is down, cancelling all running ingest jobs", serviceDisplayName); //NON-NLS
201  EventQueue.invokeLater(new Runnable() {
202  @Override
203  public void run() {
204  JOptionPane.showMessageDialog(null,
205  NbBundle.getMessage(this.getClass(), "IngestManager.cancellingIngest.msgDlg.text"),
206  NbBundle.getMessage(this.getClass(), "IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName),
207  JOptionPane.ERROR_MESSAGE);
208  }
209  });
210  }
212  }
213  };
214 
215  /*
216  * The key services for multi-user cases are currently the case database
217  * server and the Solr server. The Solr server is a key service not
218  * because search is essential, but because the coordination service
219  * (ZooKeeper) is running embedded within the Solr server.
220  */
221  Set<String> servicesList = new HashSet<>();
222  servicesList.add(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString());
223  servicesList.add(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString());
224  this.servicesMonitor.addSubscriber(servicesList, propChangeListener);
225  }
226 
231  private void subscribeToCaseEvents() {
232  Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), (PropertyChangeEvent event) -> {
233  if (event.getNewValue() != null) {
234  handleCaseOpened();
235  } else {
236  handleCaseClosed();
237  }
238  });
239  }
240 
241  /*
242  * Handles a current case opened event by clearing the ingest messages inbox
243  * and opening a remote event channel for the current case.
244  *
245  * Note that current case change events are published in a strictly
246  * serialized manner, i.e., one event at a time, synchronously.
247  */
248  void handleCaseOpened() {
249  caseIsOpen = true;
251  try {
252  Case openedCase = Case.getCurrentCase();
253  String channelPrefix = openedCase.getName();
254  if (Case.CaseType.MULTI_USER_CASE == openedCase.getCaseType()) {
255  jobEventPublisher.openRemoteEventChannel(String.format(INGEST_JOB_EVENT_CHANNEL_NAME, channelPrefix));
256  moduleEventPublisher.openRemoteEventChannel(String.format(INGEST_MODULE_EVENT_CHANNEL_NAME, channelPrefix));
257  }
258  } catch (IllegalStateException | AutopsyEventException ex) {
259  LOGGER.log(Level.SEVERE, "Failed to open remote events channel", ex); //NON-NLS
260  MessageNotifyUtil.Notify.error(NbBundle.getMessage(IngestManager.class, "IngestManager.OpenEventChannel.Fail.Title"),
261  NbBundle.getMessage(IngestManager.class, "IngestManager.OpenEventChannel.Fail.ErrMsg"));
262  }
263  }
264 
265  /*
266  * Handles a current case closed event by cancelling all ingest jobs for the
267  * case, closing the remote event channel for the case, and clearing the
268  * ingest messages inbox.
269  *
270  * Note that current case change events are published in a strictly
271  * serialized manner, i.e., one event at a time, synchronously.
272  */
273  void handleCaseClosed() {
274  /*
275  * TODO (JIRA-2227): IngestManager should wait for cancelled ingest jobs
276  * to complete when a case is closed.
277  */
278  this.cancelAllIngestJobs(IngestJob.CancellationReason.CASE_CLOSED);
281  caseIsOpen = false;
283  }
284 
293  }
294 
301  public void queueIngestJob(Collection<Content> dataSources, IngestJobSettings settings) {
302  if (caseIsOpen) {
303  IngestJob job = new IngestJob(dataSources, settings, RuntimeProperties.runningWithGUI());
304  if (job.hasIngestPipeline()) {
305  long taskId = nextIngestManagerTaskId.incrementAndGet();
306  Future<Void> task = startIngestJobsExecutor.submit(new StartIngestJobTask(taskId, job));
307  startIngestJobFutures.put(taskId, task);
308  }
309  }
310  }
311 
321  public IngestJobStartResult beginIngestJob(Collection<Content> dataSources, IngestJobSettings settings) {
322  if (caseIsOpen) {
323  IngestJob job = new IngestJob(dataSources, settings, RuntimeProperties.runningWithGUI());
324  if (job.hasIngestPipeline()) {
325  return startIngestJob(job);
326  }
327  return new IngestJobStartResult(null, new IngestManagerException("No ingest pipeline created, likely due to no ingest modules being enabled"), null); //NON-NLS
328  }
329  return new IngestJobStartResult(null, new IngestManagerException("No case open"), null); //NON-NLS
330  }
331 
340  @NbBundle.Messages({
341  "IngestManager.startupErr.dlgTitle=Ingest Module Startup Failure",
342  "IngestManager.startupErr.dlgMsg=Unable to start up one or more ingest modules, ingest cancelled.",
343  "IngestManager.startupErr.dlgSolution=Please disable the failed modules or fix the errors before restarting ingest.",
344  "IngestManager.startupErr.dlgErrorList=Errors:"
345  })
347  List<IngestModuleError> errors = null;
348  if (caseIsOpen) {
350  try {
353  EventQueue.invokeLater(new Runnable() {
354  @Override
355  public void run() {
356  String serviceDisplayName = ServicesMonitor.Service.REMOTE_CASE_DATABASE.getDisplayName();
357  JOptionPane.showMessageDialog(null,
358  NbBundle.getMessage(this.getClass(), "IngestManager.cancellingIngest.msgDlg.text"),
359  NbBundle.getMessage(this.getClass(), "IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName),
360  JOptionPane.ERROR_MESSAGE);
361  }
362  });
363  }
364  return new IngestJobStartResult(null, new IngestManagerException("Ingest aborted. Remote database is down"), Collections.<IngestModuleError>emptyList()); //NON-NLS
365  }
367  return new IngestJobStartResult(null, new IngestManagerException("Database server is down", ex), Collections.<IngestModuleError>emptyList()); //NON-NLS
368  }
369  }
370 
371  if (!ingestMonitor.isRunning()) {
372  ingestMonitor.start();
373  }
374 
375  ingestJobsById.put(job.getId(), job);
376  errors = job.start();
377  if (errors.isEmpty()) {
378  this.fireIngestJobStarted(job.getId());
379  IngestManager.LOGGER.log(Level.INFO, "Ingest job {0} started", job.getId()); //NON-NLS
380  } else {
381  this.ingestJobsById.remove(job.getId());
382  for (IngestModuleError error : errors) {
383  LOGGER.log(Level.SEVERE, String.format("Error starting %s ingest module for job %d", error.getModuleDisplayName(), job.getId()), error.getThrowable()); //NON-NLS
384  }
385  IngestManager.LOGGER.log(Level.SEVERE, "Ingest job {0} could not be started", job.getId()); //NON-NLS
387  final StringBuilder message = new StringBuilder(1024);
388  message.append(Bundle.IngestManager_startupErr_dlgMsg()).append("\n"); //NON-NLS
389  message.append(Bundle.IngestManager_startupErr_dlgSolution()).append("\n\n"); //NON-NLS
390  message.append(Bundle.IngestManager_startupErr_dlgErrorList()).append("\n"); //NON-NLS
391  for (IngestModuleError error : errors) {
392  String moduleName = error.getModuleDisplayName();
393  String errorMessage = error.getThrowable().getLocalizedMessage();
394  message.append(moduleName).append(": ").append(errorMessage).append("\n"); //NON-NLS
395  }
396  message.append("\n\n");
397  EventQueue.invokeLater(() -> {
398  JOptionPane.showMessageDialog(null, message, Bundle.IngestManager_startupErr_dlgTitle(), JOptionPane.ERROR_MESSAGE);
399  });
400  }
401  return new IngestJobStartResult(null, new IngestManagerException("Errors occurred while starting ingest"), errors); //NON-NLS
402  }
403  }
404 
405  return new IngestJobStartResult(job, null, errors);
406  }
407 
413  void finishIngestJob(IngestJob job) {
414  long jobId = job.getId();
415  ingestJobsById.remove(jobId);
416  if (!job.isCancelled()) {
417  IngestManager.LOGGER.log(Level.INFO, "Ingest job {0} completed", jobId); //NON-NLS
418  fireIngestJobCompleted(jobId);
419  } else {
420  IngestManager.LOGGER.log(Level.INFO, "Ingest job {0} cancelled", jobId); //NON-NLS
421  fireIngestJobCancelled(jobId);
422  }
423  }
424 
431  public boolean isIngestRunning() {
432  return !ingestJobsById.isEmpty();
433  }
434 
441  startIngestJobFutures.values().forEach((handle) -> {
442  handle.cancel(true);
443  });
444  this.ingestJobsById.values().forEach((job) -> {
445  job.cancel(reason);
446  });
447  }
448 
454  public void addIngestJobEventListener(final PropertyChangeListener listener) {
455  jobEventPublisher.addSubscriber(INGEST_JOB_EVENT_NAMES, listener);
456  }
457 
463  public void removeIngestJobEventListener(final PropertyChangeListener listener) {
464  jobEventPublisher.removeSubscriber(INGEST_JOB_EVENT_NAMES, listener);
465  }
466 
472  public void addIngestModuleEventListener(final PropertyChangeListener listener) {
473  moduleEventPublisher.addSubscriber(INGEST_MODULE_EVENT_NAMES, listener);
474  }
475 
481  public void removeIngestModuleEventListener(final PropertyChangeListener listener) {
482  moduleEventPublisher.removeSubscriber(INGEST_MODULE_EVENT_NAMES, listener);
483  }
484 
490  void fireIngestJobStarted(long ingestJobId) {
491  AutopsyEvent event = new AutopsyEvent(IngestJobEvent.STARTED.toString(), ingestJobId, null);
492  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
493  }
494 
500  void fireIngestJobCompleted(long ingestJobId) {
501  AutopsyEvent event = new AutopsyEvent(IngestJobEvent.COMPLETED.toString(), ingestJobId, null);
502  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
503  }
504 
510  void fireIngestJobCancelled(long ingestJobId) {
511  AutopsyEvent event = new AutopsyEvent(IngestJobEvent.CANCELLED.toString(), ingestJobId, null);
512  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
513  }
514 
523  void fireDataSourceAnalysisStarted(long ingestJobId, long dataSourceIngestJobId, Content dataSource) {
524  AutopsyEvent event = new DataSourceAnalysisStartedEvent(ingestJobId, dataSourceIngestJobId, dataSource);
525  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
526  }
527 
536  void fireDataSourceAnalysisCompleted(long ingestJobId, long dataSourceIngestJobId, Content dataSource) {
537  AutopsyEvent event = new DataSourceAnalysisCompletedEvent(ingestJobId, dataSourceIngestJobId, dataSource, DataSourceAnalysisCompletedEvent.Reason.ANALYSIS_COMPLETED);
538  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
539  }
540 
549  void fireDataSourceAnalysisCancelled(long ingestJobId, long dataSourceIngestJobId, Content dataSource) {
550  AutopsyEvent event = new DataSourceAnalysisCompletedEvent(ingestJobId, dataSourceIngestJobId, dataSource, DataSourceAnalysisCompletedEvent.Reason.ANALYSIS_CANCELLED);
551  eventPublishingExecutor.submit(new PublishEventTask(event, jobEventPublisher));
552  }
553 
560  void fireFileIngestDone(AbstractFile file) {
561  AutopsyEvent event = new FileAnalyzedEvent(file);
562  eventPublishingExecutor.submit(new PublishEventTask(event, moduleEventPublisher));
563  }
564 
572  void fireIngestModuleDataEvent(ModuleDataEvent moduleDataEvent) {
573  AutopsyEvent event = new BlackboardPostEvent(moduleDataEvent);
574  eventPublishingExecutor.submit(new PublishEventTask(event, moduleEventPublisher));
575  }
576 
584  void fireIngestModuleContentEvent(ModuleContentEvent moduleContentEvent) {
585  AutopsyEvent event = new ContentChangedEvent(moduleContentEvent);
586  eventPublishingExecutor.submit(new PublishEventTask(event, moduleEventPublisher));
587  }
588 
594  void initIngestMessageInbox() {
595  synchronized (this.ingestMessageBoxLock) {
596  ingestMessageBox = IngestMessageTopComponent.findInstance();
597  }
598  }
599 
605  void postIngestMessage(IngestMessage message) {
606  synchronized (this.ingestMessageBoxLock) {
607  if (ingestMessageBox != null && RuntimeProperties.runningWithGUI()) {
608  if (message.getMessageType() != IngestMessage.MessageType.ERROR && message.getMessageType() != IngestMessage.MessageType.WARNING) {
609  ingestMessageBox.displayMessage(message);
610  } else {
611  long errorPosts = ingestErrorMessagePosts.incrementAndGet();
612  if (errorPosts <= MAX_ERROR_MESSAGE_POSTS) {
613  ingestMessageBox.displayMessage(message);
614  } else if (errorPosts == MAX_ERROR_MESSAGE_POSTS + 1) {
615  IngestMessage errorMessageLimitReachedMessage = IngestMessage.createErrorMessage(
616  NbBundle.getMessage(this.getClass(), "IngestManager.IngestMessage.ErrorMessageLimitReached.title"),
617  NbBundle.getMessage(this.getClass(), "IngestManager.IngestMessage.ErrorMessageLimitReached.subject"),
618  NbBundle.getMessage(this.getClass(), "IngestManager.IngestMessage.ErrorMessageLimitReached.msg", MAX_ERROR_MESSAGE_POSTS));
619  ingestMessageBox.displayMessage(errorMessageLimitReachedMessage);
620  }
621  }
622  }
623  }
624  }
625 
626  /*
627  * Clears the ingest messages inbox.
628  */
629  private void clearIngestMessageBox() {
630  synchronized (this.ingestMessageBoxLock) {
631  if (null != ingestMessageBox) {
632  ingestMessageBox.clearMessages();
633  }
635  }
636  }
637 
649  void setIngestTaskProgress(DataSourceIngestTask task, String ingestModuleDisplayName) {
650  ingestThreadActivitySnapshots.put(task.getThreadId(), new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJob().getId(), ingestModuleDisplayName, task.getDataSource()));
651  }
652 
664  void setIngestTaskProgress(FileIngestTask task, String ingestModuleDisplayName) {
665  IngestThreadActivitySnapshot prevSnap = ingestThreadActivitySnapshots.get(task.getThreadId());
666  IngestThreadActivitySnapshot newSnap = new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJob().getId(), ingestModuleDisplayName, task.getDataSource(), task.getFile());
667  ingestThreadActivitySnapshots.put(task.getThreadId(), newSnap);
668  incrementModuleRunTime(prevSnap.getActivity(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
669  }
670 
678  void setIngestTaskProgressCompleted(DataSourceIngestTask task) {
679  ingestThreadActivitySnapshots.put(task.getThreadId(), new IngestThreadActivitySnapshot(task.getThreadId()));
680  }
681 
689  void setIngestTaskProgressCompleted(FileIngestTask task) {
690  IngestThreadActivitySnapshot prevSnap = ingestThreadActivitySnapshots.get(task.getThreadId());
691  IngestThreadActivitySnapshot newSnap = new IngestThreadActivitySnapshot(task.getThreadId());
692  ingestThreadActivitySnapshots.put(task.getThreadId(), newSnap);
693  incrementModuleRunTime(prevSnap.getActivity(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
694  }
695 
702  private void incrementModuleRunTime(String moduleDisplayName, Long duration) {
703  if (moduleDisplayName.equals("IDLE")) { //NON-NLS
704  return;
705  }
706 
707  synchronized (ingestModuleRunTimes) {
708  Long prevTimeL = ingestModuleRunTimes.get(moduleDisplayName);
709  long prevTime = 0;
710  if (prevTimeL != null) {
711  prevTime = prevTimeL;
712  }
713  prevTime += duration;
714  ingestModuleRunTimes.put(moduleDisplayName, prevTime);
715  }
716  }
717 
723  Map<String, Long> getModuleRunTimes() {
724  synchronized (ingestModuleRunTimes) {
725  Map<String, Long> times = new HashMap<>(ingestModuleRunTimes);
726  return times;
727  }
728  }
729 
736  List<IngestThreadActivitySnapshot> getIngestThreadActivitySnapshots() {
737  return new ArrayList<>(ingestThreadActivitySnapshots.values());
738  }
739 
745  List<DataSourceIngestJob.Snapshot> getIngestJobSnapshots() {
746  List<DataSourceIngestJob.Snapshot> snapShots = new ArrayList<>();
747  ingestJobsById.values().forEach((job) -> {
748  snapShots.addAll(job.getDataSourceIngestJobSnapshots());
749  });
750  return snapShots;
751  }
752 
759  long getFreeDiskSpace() {
760  if (ingestMonitor != null) {
761  return ingestMonitor.getFreeSpace();
762  } else {
763  return -1;
764  }
765  }
766 
770  private final class StartIngestJobTask implements Callable<Void> {
771 
772  private final long threadId;
773  private final IngestJob job;
774  private ProgressHandle progress;
775 
776  StartIngestJobTask(long threadId, IngestJob job) {
777  this.threadId = threadId;
778  this.job = job;
779  }
780 
781  @Override
782  public Void call() {
783  try {
784  if (Thread.currentThread().isInterrupted()) {
785  ingestJobsById.remove(job.getId());
786  return null;
787  }
788 
790  final String displayName = NbBundle.getMessage(this.getClass(), "IngestManager.StartIngestJobsTask.run.displayName");
791  this.progress = ProgressHandle.createHandle(displayName, new Cancellable() {
792  @Override
793  public boolean cancel() {
794  if (progress != null) {
795  progress.setDisplayName(NbBundle.getMessage(this.getClass(), "IngestManager.StartIngestJobsTask.run.cancelling", displayName));
796  }
797  Future<?> handle = startIngestJobFutures.remove(threadId);
798  handle.cancel(true);
799  return true;
800  }
801  });
802  progress.start();
803  }
804 
805  startIngestJob(job);
806  return null;
807 
808  } finally {
809  if (null != progress) {
810  progress.finish();
811  }
812  startIngestJobFutures.remove(threadId);
813  }
814  }
815 
816  }
817 
821  private final class ExecuteIngestJobTasksTask implements Runnable {
822 
823  private final long threadId;
824  private final IngestTaskQueue tasks;
825 
826  ExecuteIngestJobTasksTask(long threadId, IngestTaskQueue tasks) {
827  this.threadId = threadId;
828  this.tasks = tasks;
829  }
830 
831  @Override
832  public void run() {
833  while (true) {
834  try {
835  IngestTask task = tasks.getNextTask(); // Blocks.
836  task.execute(threadId);
837  } catch (InterruptedException ex) {
838  break;
839  }
840  if (Thread.currentThread().isInterrupted()) {
841  break;
842  }
843  }
844  }
845  }
846 
850  private static final class PublishEventTask implements Runnable {
851 
852  private final AutopsyEvent event;
854 
863  this.event = event;
864  this.publisher = publisher;
865  }
866 
867  @Override
868  public void run() {
869  publisher.publish(event);
870  }
871 
872  }
873 
878  @Immutable
879  static final class IngestThreadActivitySnapshot {
880 
881  private final long threadId;
882  private final Date startTime;
883  private final String activity;
884  private final String dataSourceName;
885  private final String fileName;
886  private final long jobId;
887 
895  IngestThreadActivitySnapshot(long threadId) {
896  this.threadId = threadId;
897  startTime = new Date();
898  this.activity = NbBundle.getMessage(this.getClass(), "IngestManager.IngestThreadActivitySnapshot.idleThread");
899  this.dataSourceName = "";
900  this.fileName = "";
901  this.jobId = 0;
902  }
903 
914  IngestThreadActivitySnapshot(long threadId, long jobId, String activity, Content dataSource) {
915  this.threadId = threadId;
916  this.jobId = jobId;
917  startTime = new Date();
918  this.activity = activity;
919  this.dataSourceName = dataSource.getName();
920  this.fileName = "";
921  }
922 
935  IngestThreadActivitySnapshot(long threadId, long jobId, String activity, Content dataSource, AbstractFile file) {
936  this.threadId = threadId;
937  this.jobId = jobId;
938  startTime = new Date();
939  this.activity = activity;
940  this.dataSourceName = dataSource.getName();
941  this.fileName = file.getName();
942  }
943 
949  long getIngestJobId() {
950  return jobId;
951  }
952 
958  long getThreadId() {
959  return threadId;
960  }
961 
967  Date getStartTime() {
968  return startTime;
969  }
970 
976  String getActivity() {
977  return activity;
978  }
979 
987  String getDataSourceName() {
988  return dataSourceName;
989  }
990 
996  String getFileName() {
997  return fileName;
998  }
999 
1000  }
1001 
1005  public enum IngestJobEvent {
1006 
1043  }
1044 
1048  public enum IngestModuleEvent {
1049 
1072  }
1073 
1077  public final static class IngestManagerException extends Exception {
1078 
1079  private static final long serialVersionUID = 1L;
1080 
1086  private IngestManagerException(String message) {
1087  super(message);
1088  }
1089 
1096  private IngestManagerException(String message, Throwable cause) {
1097  super(message, cause);
1098  }
1099  }
1100 
1109  @Deprecated
1110  public static void addPropertyChangeListener(final PropertyChangeListener listener) {
1113  }
1114 
1123  @Deprecated
1124  public static void removePropertyChangeListener(final PropertyChangeListener listener) {
1127  }
1128 
1139  @Deprecated
1140  public synchronized IngestJob startIngestJob(Collection<Content> dataSources, IngestJobSettings settings) {
1141  return beginIngestJob(dataSources, settings).getJob();
1142  }
1143 
1150  @Deprecated
1151  public void cancelAllIngestJobs() {
1153  }
1154 
1155 }
final ConcurrentHashMap< String, Long > ingestModuleRunTimes
final Map< Long, Future< Void > > startIngestJobFutures
void removeIngestModuleEventListener(final PropertyChangeListener listener)
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:161
void incrementModuleRunTime(String moduleDisplayName, Long duration)
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
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:381
final AutopsyEventPublisher jobEventPublisher

Copyright © 2012-2016 Basis Technology. Generated on: Tue Jun 13 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.