19 package org.sleuthkit.autopsy.ingest;
 
   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.io.Serializable;
 
   26 import java.util.ArrayList;
 
   27 import java.util.Collection;
 
   28 import java.util.Collections;
 
   29 import java.util.Date;
 
   30 import java.util.EnumSet;
 
   31 import java.util.HashMap;
 
   32 import java.util.HashSet;
 
   33 import java.util.List;
 
   36 import java.util.concurrent.Callable;
 
   37 import java.util.concurrent.ConcurrentHashMap;
 
   38 import java.util.concurrent.ExecutorService;
 
   39 import java.util.concurrent.Executors;
 
   40 import java.util.concurrent.Future;
 
   41 import java.util.concurrent.atomic.AtomicLong;
 
   42 import java.util.logging.Level;
 
   43 import java.util.stream.Collectors;
 
   44 import java.util.stream.Stream;
 
   45 import javax.annotation.concurrent.GuardedBy;
 
   46 import javax.annotation.concurrent.Immutable;
 
   47 import javax.annotation.concurrent.ThreadSafe;
 
   48 import javax.swing.JOptionPane;
 
   49 import org.netbeans.api.progress.ProgressHandle;
 
   50 import org.openide.util.Cancellable;
 
   51 import org.openide.util.NbBundle;
 
   52 import org.openide.windows.WindowManager;
 
  119     @GuardedBy(
"IngestManager.class")
 
  123     private final ExecutorService 
startIngestJobsExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-start-ingest-jobs-%d").build()); 
 
  128     private final ExecutorService 
eventPublishingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IM-ingest-events-%d").build()); 
 
  147         if (null == instance) {
 
  149             instance.subscribeToServiceMonitorEvents();
 
  150             instance.subscribeToCaseEvents();
 
  188         PropertyChangeListener propChangeListener = (PropertyChangeEvent evt) -> {
 
  203                 logger.log(Level.SEVERE, 
"Service {0} is down, cancelling all running ingest jobs", serviceDisplayName); 
 
  205                     EventQueue.invokeLater(
new Runnable() {
 
  208                             JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  209                                     NbBundle.getMessage(this.getClass(), 
"IngestManager.cancellingIngest.msgDlg.text"),
 
  210                                     NbBundle.getMessage(this.getClass(), 
"IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName),
 
  211                                     JOptionPane.ERROR_MESSAGE);
 
  225         Set<String> servicesList = 
new HashSet<>();
 
  237             if (event.getNewValue() != null) {
 
  252     void handleCaseOpened() {
 
  257             String channelPrefix = openedCase.
getName();
 
  262         } 
catch (NoCurrentCaseException | AutopsyEventException ex) {
 
  263             logger.log(Level.SEVERE, 
"Failed to open remote events channel", ex); 
 
  264             MessageNotifyUtil.Notify.error(NbBundle.getMessage(
IngestManager.class, 
"IngestManager.OpenEventChannel.Fail.Title"),
 
  265                     NbBundle.getMessage(
IngestManager.class, 
"IngestManager.OpenEventChannel.Fail.ErrMsg"));
 
  277     void handleCaseClosed() {
 
  308             if (job.hasIngestPipeline()) {
 
  327             if (job.hasIngestPipeline()) {
 
  347             if (job.hasIngestPipeline()) {
 
  364         "IngestManager.startupErr.dlgTitle=Ingest Module Startup Failure",
 
  365         "IngestManager.startupErr.dlgMsg=Unable to start up one or more ingest modules, ingest cancelled.",
 
  366         "IngestManager.startupErr.dlgSolution=Please disable the failed modules or fix the errors before restarting ingest.",
 
  367         "IngestManager.startupErr.dlgErrorList=Errors:" 
  370         List<IngestModuleError> errors = null;
 
  381                         EventQueue.invokeLater(
new Runnable() {
 
  385                                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  386                                         NbBundle.getMessage(this.getClass(), 
"IngestManager.cancellingIngest.msgDlg.text"),
 
  387                                         NbBundle.getMessage(this.getClass(), 
"IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName),
 
  388                                         JOptionPane.ERROR_MESSAGE);
 
  407         errors = job.start();
 
  408         if (errors.isEmpty()) {
 
  409             this.fireIngestJobStarted(job.
getId());
 
  415                 logger.log(Level.SEVERE, String.format(
"Error starting %s ingest module for job %d", error.getModuleDisplayName(), job.
getId()), error.getThrowable()); 
 
  419                 final StringBuilder message = 
new StringBuilder(1024);
 
  420                 message.append(Bundle.IngestManager_startupErr_dlgMsg()).append(
"\n"); 
 
  421                 message.append(Bundle.IngestManager_startupErr_dlgSolution()).append(
"\n\n"); 
 
  422                 message.append(Bundle.IngestManager_startupErr_dlgErrorList()).append(
"\n"); 
 
  424                     String moduleName = error.getModuleDisplayName();
 
  425                     String errorMessage = error.getThrowable().getLocalizedMessage();
 
  426                     message.append(moduleName).append(
": ").append(errorMessage).append(
"\n"); 
 
  428                 message.append(
"\n\n");
 
  429                 EventQueue.invokeLater(() -> {
 
  430                     JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), message, Bundle.IngestManager_startupErr_dlgTitle(), JOptionPane.ERROR_MESSAGE);
 
  445         long jobId = job.
getId();
 
  450             IngestManager.logger.log(Level.INFO, 
"Ingest job {0} completed", jobId); 
 
  451             fireIngestJobCompleted(jobId);
 
  453             IngestManager.logger.log(Level.INFO, 
"Ingest job {0} cancelled", jobId); 
 
  454             fireIngestJobCancelled(jobId);
 
  573     void fireIngestJobStarted(
long ingestJobId) {
 
  583     void fireIngestJobCompleted(
long ingestJobId) {
 
  584         AutopsyEvent 
event = 
new AutopsyEvent(IngestJobEvent.COMPLETED.toString(), ingestJobId, null);
 
  593     void fireIngestJobCancelled(
long ingestJobId) {
 
  594         AutopsyEvent 
event = 
new AutopsyEvent(IngestJobEvent.CANCELLED.toString(), ingestJobId, null);
 
  606     void fireDataSourceAnalysisStarted(
long ingestJobId, 
long dataSourceIngestJobId, Content dataSource) {
 
  607         AutopsyEvent 
event = 
new DataSourceAnalysisStartedEvent(ingestJobId, dataSourceIngestJobId, dataSource);
 
  619     void fireDataSourceAnalysisCompleted(
long ingestJobId, 
long dataSourceIngestJobId, Content dataSource) {
 
  620         AutopsyEvent 
event = 
new DataSourceAnalysisCompletedEvent(ingestJobId, dataSourceIngestJobId, dataSource, DataSourceAnalysisCompletedEvent.Reason.ANALYSIS_COMPLETED);
 
  632     void fireDataSourceAnalysisCancelled(
long ingestJobId, 
long dataSourceIngestJobId, Content dataSource) {
 
  633         AutopsyEvent 
event = 
new DataSourceAnalysisCompletedEvent(ingestJobId, dataSourceIngestJobId, dataSource, DataSourceAnalysisCompletedEvent.Reason.ANALYSIS_CANCELLED);
 
  643     void fireFileIngestDone(AbstractFile file) {
 
  644         AutopsyEvent 
event = 
new FileAnalyzedEvent(file);
 
  655     void fireIngestModuleDataEvent(ModuleDataEvent moduleDataEvent) {
 
  656         AutopsyEvent 
event = 
new BlackboardPostEvent(moduleDataEvent);
 
  667     void fireIngestModuleContentEvent(ModuleContentEvent moduleContentEvent) {
 
  668         AutopsyEvent 
event = 
new ContentChangedEvent(moduleContentEvent);
 
  677     void initIngestMessageInbox() {
 
  688     void postIngestMessage(IngestMessage message) {
 
  691                 if (message.getMessageType() != IngestMessage.MessageType.ERROR && message.getMessageType() != IngestMessage.MessageType.WARNING) {
 
  695                     if (errorPosts <= MAX_ERROR_MESSAGE_POSTS) {
 
  697                     } 
else if (errorPosts == MAX_ERROR_MESSAGE_POSTS + 1) {
 
  698                         IngestMessage errorMessageLimitReachedMessage = IngestMessage.createErrorMessage(
 
  699                                 NbBundle.getMessage(
this.getClass(), 
"IngestManager.IngestMessage.ErrorMessageLimitReached.title"),
 
  700                                 NbBundle.getMessage(
this.getClass(), 
"IngestManager.IngestMessage.ErrorMessageLimitReached.subject"),
 
  701                                 NbBundle.getMessage(
this.getClass(), 
"IngestManager.IngestMessage.ErrorMessageLimitReached.msg", 
MAX_ERROR_MESSAGE_POSTS));
 
  732     void setIngestTaskProgress(DataSourceIngestTask task, String ingestModuleDisplayName) {
 
  733         ingestThreadActivitySnapshots.put(task.getThreadId(), 
new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJob().getId(), ingestModuleDisplayName, task.getDataSource()));
 
  747     void setIngestTaskProgress(FileIngestTask task, String ingestModuleDisplayName) {
 
  749         IngestThreadActivitySnapshot newSnap = 
new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJob().getId(), ingestModuleDisplayName, task.getDataSource(), task.getFile());
 
  751         incrementModuleRunTime(prevSnap.getActivity(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
 
  761     void setIngestTaskProgressCompleted(DataSourceIngestTask task) {
 
  772     void setIngestTaskProgressCompleted(FileIngestTask task) {
 
  774         IngestThreadActivitySnapshot newSnap = 
new IngestThreadActivitySnapshot(task.getThreadId());
 
  776         incrementModuleRunTime(prevSnap.getActivity(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
 
  786         if (moduleDisplayName.equals(
"IDLE")) { 
 
  793             if (prevTimeL != null) {
 
  794                 prevTime = prevTimeL;
 
  796             prevTime += duration;
 
  835                 snapShots.addAll(job.getDataSourceIngestJobSnapshots());
 
  847     long getFreeDiskSpace() {
 
  872                 if (Thread.currentThread().isInterrupted()) {
 
  880                     final String displayName = NbBundle.getMessage(this.getClass(), 
"IngestManager.StartIngestJobsTask.run.displayName");
 
  881                     this.progress = ProgressHandle.createHandle(displayName, 
new Cancellable() {
 
  883                         public boolean cancel() {
 
  884                             if (progress != null) {
 
  885                                 progress.setDisplayName(NbBundle.getMessage(
this.getClass(), 
"IngestManager.StartIngestJobsTask.run.cancelling", displayName));
 
  899                 if (null != progress) {
 
  914         private final BlockingIngestTaskQueue 
tasks;
 
  925                     IngestTask task = tasks.getNextTask(); 
 
  926                     task.execute(threadId);
 
  927                 } 
catch (InterruptedException ex) {
 
  930                 if (Thread.currentThread().isInterrupted()) {
 
  989             startTime = 
new Date();
 
  990             this.activity = NbBundle.getMessage(this.getClass(), 
"IngestManager.IngestThreadActivitySnapshot.idleThread");
 
  991             this.dataSourceName = 
"";
 
 1009             startTime = 
new Date();
 
 1011             this.dataSourceName = dataSource.getName();
 
 1027         IngestThreadActivitySnapshot(
long threadId, 
long jobId, String activity, Content dataSource, AbstractFile file) {
 
 1030             startTime = 
new Date();
 
 1032             this.dataSourceName = dataSource.getName();
 
 1033             this.fileName = file.getName();
 
 1041         long getIngestJobId() {
 
 1050         long getThreadId() {
 
 1059         Date getStartTime() {
 
 1068         String getActivity() {
 
 1079         String getDataSourceName() {
 
 1088         String getFileName() {
 
 1170         private static final long serialVersionUID = 1L;
 
 1188             super(message, cause);
 
final ConcurrentHashMap< String, Long > ingestModuleRunTimes
 
void addIngestModuleEventListener(Set< IngestModuleEvent > eventTypes, final PropertyChangeListener listener)
 
final Map< Long, Future< Void > > startIngestJobFutures
 
String getServiceStatus(String service)
 
void removeIngestModuleEventListener(final PropertyChangeListener listener)
 
static final String INGEST_MODULE_EVENT_CHANNEL_NAME
 
List< IngestThreadActivitySnapshot > getIngestThreadActivitySnapshots()
 
void queueIngestJob(Content dataSource, List< AbstractFile > files, IngestJobSettings settings)
 
IngestManagerException(String message, Throwable cause)
 
static synchronized IngestManager getInstance()
 
static IngestManager instance
 
final String dataSourceName
 
IngestJobStartResult startIngestJob(IngestJob job)
 
void removeIngestModuleEventListener(Set< IngestModuleEvent > eventTypes, final PropertyChangeListener listener)
 
final ExecutorService dataSourceLevelIngestJobTasksExecutor
 
static boolean runningWithGUI
 
void cancelAllIngestJobs()
 
void publish(AutopsyEvent event)
 
static void addPropertyChangeListener(final PropertyChangeListener listener)
 
IngestJobStartResult beginIngestJob(Collection< Content > dataSources, IngestJobSettings settings)
 
static final Logger logger
 
final ExecutorService eventPublishingExecutor
 
void clearIngestMessageBox()
 
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
 
void subscribeToServiceMonitorEvents()
 
boolean isIngestRunning()
 
DATA_SOURCE_ANALYSIS_COMPLETED
 
static void removePropertyChangeListener(final PropertyChangeListener listener)
 
static final Set< String > INGEST_MODULE_EVENT_NAMES
 
volatile IngestMessageTopComponent ingestMessageBox
 
void addSubscriber(PropertyChangeListener subscriber)
 
final AutopsyEventPublisher publisher
 
synchronized void closeRemoteEventChannel()
 
Map< String, Long > getModuleRunTimes()
 
final ServicesMonitor servicesMonitor
 
void removeIngestJobEventListener(final PropertyChangeListener listener)
 
void addIngestJobEventListener(Set< IngestJobEvent > eventTypes, final PropertyChangeListener listener)
 
final BlockingIngestTaskQueue tasks
 
static final String INGEST_JOB_EVENT_CHANNEL_NAME
 
static final Set< String > INGEST_JOB_EVENT_NAMES
 
final AutopsyEventPublisher moduleEventPublisher
 
static int numberOfFileIngestThreads()
 
List< DataSourceIngestJob.Snapshot > getIngestJobSnapshots()
 
synchronized void openRemoteEventChannel(String channelName)
 
void addIngestJobEventListener(final PropertyChangeListener listener)
 
final Object ingestMessageBoxLock
 
IngestManagerException(String message)
 
void queueIngestJob(Collection< Content > dataSources, IngestJobSettings settings)
 
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
 
static final int MAX_ERROR_MESSAGE_POSTS
 
final AtomicLong ingestErrorMessagePosts
 
volatile boolean caseIsOpen
 
final AtomicLong nextIngestManagerTaskId
 
int getNumberOfFileIngestThreads()
 
void addIngestModuleEventListener(final PropertyChangeListener listener)
 
synchronized static Logger getLogger(String name)
 
DATA_SOURCE_ANALYSIS_STARTED
 
static Case getCurrentCaseThrows()
 
void incrementModuleRunTime(String moduleDisplayName, Long duration)
 
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
 
synchronized IngestJob startIngestJob(Collection< Content > dataSources, IngestJobSettings settings)
 
final ConcurrentHashMap< Long, IngestThreadActivitySnapshot > ingestThreadActivitySnapshots
 
void cancelAllIngestJobs(IngestJob.CancellationReason reason)
 
final IngestMonitor ingestMonitor
 
final ExecutorService fileLevelIngestJobTasksExecutor
 
final Map< Long, IngestJob > ingestJobsById
 
void subscribeToCaseEvents()
 
final ExecutorService startIngestJobsExecutor
 
void removeIngestJobEventListener(Set< IngestJobEvent > eventTypes, final PropertyChangeListener listener)
 
final int numberOfFileIngestThreads
 
static final long serialVersionUID
 
final AutopsyEventPublisher jobEventPublisher