357 lines
16 KiB
Java

/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import javax.swing.Action;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMonitor.JobsSnapshot;
import org.sleuthkit.autopsy.guiutils.DurationCellRenderer;
import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer;
/**
* A node which represents all AutoIngestJobs of a given AutoIngestJobStatus.
* Each job with the specified status will have a child node representing it.
*/
final class AutoIngestJobsNode extends AbstractNode {
//Event bus is non static so that each instance of this will only listen to events sent to that instance
private final EventBus refreshChildrenEventBus;
@Messages({
"AutoIngestJobsNode.caseName.text=Case Name",
"AutoIngestJobsNode.dataSource.text=Data Source",
"AutoIngestJobsNode.hostName.text=Host Name",
"AutoIngestJobsNode.stage.text=Stage",
"AutoIngestJobsNode.stageTime.text=Time in Stage",
"AutoIngestJobsNode.jobCreated.text=Job Created",
"AutoIngestJobsNode.jobCompleted.text=Job Completed",
"AutoIngestJobsNode.priority.text=Prioritized",
"AutoIngestJobsNode.status.text=Status"
})
/**
* Construct a new AutoIngestJobsNode.
*/
AutoIngestJobsNode(JobsSnapshot jobsSnapshot, AutoIngestJobStatus status, EventBus eventBus) {
super(Children.create(new AutoIngestNodeChildren(jobsSnapshot, status, eventBus), false));
refreshChildrenEventBus = eventBus;
}
/**
* Refresh the contents of the AutoIngestJobsNode and all of its children.
*/
void refresh(AutoIngestNodeRefreshEvents.AutoIngestRefreshEvent refreshEvent) {
refreshChildrenEventBus.post(refreshEvent);
}
/**
* A ChildFactory for generating JobNodes.
*/
static final class AutoIngestNodeChildren extends ChildFactory<AutoIngestJob> {
private final AutoIngestJobStatus autoIngestJobStatus;
private final JobsSnapshot jobsSnapshot;
private final RefreshChildrenSubscriber refreshChildrenSubscriber = new RefreshChildrenSubscriber();
private final EventBus refreshEventBus;
/**
* Create children nodes for the AutoIngestJobsNode which will each
* represent a single AutoIngestJob
*
* @param snapshot the snapshot which contains the AutoIngestJobs
* @param status the status of the jobs being displayed
*/
AutoIngestNodeChildren(JobsSnapshot snapshot, AutoIngestJobStatus status, EventBus eventBus) {
jobsSnapshot = snapshot;
autoIngestJobStatus = status;
refreshEventBus = eventBus;
refreshChildrenSubscriber.register(refreshEventBus);
}
@Override
protected boolean createKeys(List<AutoIngestJob> list) {
List<AutoIngestJob> jobs;
switch (autoIngestJobStatus) {
case PENDING_JOB:
jobs = jobsSnapshot.getPendingJobs();
jobs.sort(new AutoIngestJob.PriorityComparator());
break;
case RUNNING_JOB:
jobs = jobsSnapshot.getRunningJobs();
break;
case COMPLETED_JOB:
jobs = jobsSnapshot.getCompletedJobs();
break;
default:
jobs = new ArrayList<>();
}
if (jobs != null && jobs.size() > 0) {
list.addAll(jobs);
}
return true;
}
@Override
protected Node createNodeForKey(AutoIngestJob key) {
return new JobNode(key, autoIngestJobStatus, refreshEventBus);
}
/**
* Class which registers with EventBus and causes child nodes which
* exist to be refreshed.
*/
private class RefreshChildrenSubscriber {
/**
* Construct a RefreshChildrenSubscriber
*/
private RefreshChildrenSubscriber() {
}
/**
* Registers this subscriber with the specified EventBus to receive
* events posted to it.
*
* @param eventBus - the EventBus to register this subscriber to
*/
private void register(EventBus eventBus) {
eventBus.register(this);
}
/**
* Receive events which implement the AutoIngestRefreshEvent
* interface from the EventBus which this class is registered to,
* and refresh the children created by this factory.
*
*
* @param refreshEvent the AutoIngestRefreshEvent which was received
*/
@Subscribe
private void subscribeToRefresh(AutoIngestNodeRefreshEvents.AutoIngestRefreshEvent refreshEvent) {
//Ignore netbeans suggesting this isn't being used, it is used behind the scenes by the EventBus
//RefreshChildrenEvents can change which children are present however
//RefreshJobEvents and RefreshCaseEvents can still change the order we want to display them in
refresh(true);
}
}
}
/**
* A node which represents a single auto ingest job.
*/
static final class JobNode extends AbstractNode {
private final AutoIngestJob autoIngestJob;
private final AutoIngestJobStatus jobStatus;
private final RefreshNodeSubscriber refreshNodeSubscriber = new RefreshNodeSubscriber();
/**
* Construct a new JobNode to represent an AutoIngestJob and its status.
*
* @param job - the AutoIngestJob being represented by this node
* @param status - the current status of the AutoIngestJob being
* represented
*/
JobNode(AutoIngestJob job, AutoIngestJobStatus status, EventBus eventBus) {
super(Children.LEAF);
jobStatus = status;
autoIngestJob = job;
setName(autoIngestJob.toString()); //alows job to be uniquely found by name since it will involve a hash of the AutoIngestJob
setDisplayName(autoIngestJob.getManifest().getCaseName()); //displays user friendly case name as name
refreshNodeSubscriber.register(eventBus);
}
/**
* Get the AutoIngestJob which this node represents.
*
* @return autoIngestJob
*/
AutoIngestJob getAutoIngestJob() {
return autoIngestJob;
}
@Override
@Messages({"AutoIngestJobsNode.prioritized.true=Yes",
"AutoIngestJobsNode.prioritized.false=No"
})
protected Sheet createSheet() {
Sheet s = super.createSheet();
Sheet.Set ss = s.get(Sheet.PROPERTIES);
if (ss == null) {
ss = Sheet.createPropertiesSet();
s.put(ss);
}
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_caseName_text(), Bundle.AutoIngestJobsNode_caseName_text(), Bundle.AutoIngestJobsNode_caseName_text(),
autoIngestJob.getManifest().getCaseName()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_dataSource_text(), Bundle.AutoIngestJobsNode_dataSource_text(), Bundle.AutoIngestJobsNode_dataSource_text(),
autoIngestJob.getManifest().getDataSourcePath().getFileName().toString()));
switch (jobStatus) {
case PENDING_JOB:
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(),
autoIngestJob.getManifest().getDateFileCreated()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(),
autoIngestJob.getPriority() > 0 ? Bundle.AutoIngestJobsNode_prioritized_true() : Bundle.AutoIngestJobsNode_prioritized_false()));
break;
case RUNNING_JOB:
AutoIngestJob.StageDetails status = autoIngestJob.getProcessingStageDetails();
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_hostName_text(), Bundle.AutoIngestJobsNode_hostName_text(), Bundle.AutoIngestJobsNode_hostName_text(),
autoIngestJob.getProcessingHostName()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_stage_text(), Bundle.AutoIngestJobsNode_stage_text(), Bundle.AutoIngestJobsNode_stage_text(),
status.getDescription()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_stageTime_text(), Bundle.AutoIngestJobsNode_stageTime_text(), Bundle.AutoIngestJobsNode_stageTime_text(),
DurationCellRenderer.longToDurationString((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime()))));
break;
case COMPLETED_JOB:
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(),
autoIngestJob.getManifest().getDateFileCreated()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_jobCompleted_text(), Bundle.AutoIngestJobsNode_jobCompleted_text(), Bundle.AutoIngestJobsNode_jobCompleted_text(),
autoIngestJob.getCompletedDate()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_status_text(), Bundle.AutoIngestJobsNode_status_text(), Bundle.AutoIngestJobsNode_status_text(),
autoIngestJob.getErrorsOccurred() ? StatusIconCellRenderer.Status.WARNING : StatusIconCellRenderer.Status.OK));
break;
default:
}
return s;
}
@Override
public Action[] getActions(boolean context) {
List<Action> actions = new ArrayList<>();
if (AutoIngestDashboard.isAdminAutoIngestDashboard()) {
switch (jobStatus) {
case PENDING_JOB:
actions.add(new PrioritizationAction.PrioritizeJobAction(autoIngestJob));
actions.add(new PrioritizationAction.PrioritizeCaseAction(autoIngestJob));
PrioritizationAction.DeprioritizeJobAction deprioritizeJobAction = new PrioritizationAction.DeprioritizeJobAction(autoIngestJob);
deprioritizeJobAction.setEnabled(autoIngestJob.getPriority() > 0);
actions.add(deprioritizeJobAction);
PrioritizationAction.DeprioritizeCaseAction deprioritizeCaseAction = new PrioritizationAction.DeprioritizeCaseAction(autoIngestJob);
deprioritizeCaseAction.setEnabled(autoIngestJob.getPriority() > 0);
actions.add(deprioritizeCaseAction);
break;
case RUNNING_JOB:
actions.add(new AutoIngestAdminActions.ProgressDialogAction());
actions.add(new AutoIngestAdminActions.CancelJobAction());
actions.add(new AutoIngestAdminActions.CancelModuleAction());
break;
case COMPLETED_JOB:
actions.add(new AutoIngestAdminActions.ReprocessJobAction());
actions.add(new AutoIngestAdminActions.DeleteCaseAction());
actions.add(new AutoIngestAdminActions.ShowCaseLogAction());
break;
default:
}
}
return actions.toArray(new Action[actions.size()]);
}
/**
* Class which registers with EventBus and causes specific nodes to have
* their properties to be refreshed.
*/
private class RefreshNodeSubscriber {
/**
* Constructs a RefreshNodeSubscriber
*/
private RefreshNodeSubscriber() {
}
/**
* Registers this subscriber with the specified EventBus to receive
* events posted to it.
*
* @param eventBus - the EventBus to register this subscriber to
*/
private void register(EventBus eventBus) {
eventBus.register(this);
}
/**
* Receive events of type RefreshJobEvent from the EventBus which
* this class is registered to and refresh the nodes properties if
* it is the node for the job specified in the event.
*
* @param refreshEvent the RefreshJobEvent which was received
*/
@Subscribe
private void subscribeToRefreshJob(AutoIngestNodeRefreshEvents.RefreshJobEvent refreshEvent) {
//Ignore netbeans suggesting this isn't being used, it is used behind the scenes by the EventBus
if (getAutoIngestJob().equals(refreshEvent.getJobToRefresh())) {
setSheet(createSheet());
}
}
/**
* Receive events of type RefreshCaseEvent from the EventBus which
* this class is registered to and refresh the nodes which have jobs
* which are members of case specified in the event.
*
* @param refreshEvent the RefreshCaseEvent which was received
*/
@Subscribe
private void subscribeToRefreshCase(AutoIngestNodeRefreshEvents.RefreshCaseEvent refreshEvent) {
//Ignore netbeans suggesting this isn't being used, it is used behind the scenes by the EventBus
if (getAutoIngestJob().getManifest().getCaseName().equals(refreshEvent.getCaseToRefresh())) {
setSheet(createSheet());
}
}
/**
* Refresh the properties of all running jobs anytime a
* RefreshChildrenEvent is received so that stages and times stay up
* to date.
*
* @param refreshEvent - the RefreshChildrenEvent which was received
*/
private void subscribeToRefreshChildren(AutoIngestNodeRefreshEvents.RefreshChildrenEvent refreshEvent) {
//Ignore netbeans suggesting this isn't being used, it is used behind the scenes by the EventBus
if (jobStatus == AutoIngestJobStatus.RUNNING_JOB) {
setSheet(createSheet());
}
}
}
}
/**
* An enumeration used to indicate the current status of an auto ingest job
* node.
*/
enum AutoIngestJobStatus {
PENDING_JOB, //NON-NLS
RUNNING_JOB, //NON-NLS
COMPLETED_JOB //NON-NLS
}
}