2017-09-07 12:18:17 -04:00

893 lines
43 KiB
Java
Executable File

/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 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.util.concurrent.ThreadFactoryBuilder;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import javax.swing.DefaultListSelectionModel;
import java.awt.Color;
import java.beans.PropertyChangeEvent;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.core.ServicesMonitor;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMonitor.JobsSnapshot;
/**
* A dashboard for monitoring an automated ingest cluster.
*/
public final class AutoIngestDashboard extends JPanel implements Observer {
private static final long serialVersionUID = 1L;
private static final int GENERIC_COL_MIN_WIDTH = 30;
private static final int GENERIC_COL_MAX_WIDTH = 2000;
private static final int PENDING_TABLE_COL_PREFERRED_WIDTH = 280;
private static final int RUNNING_TABLE_COL_PREFERRED_WIDTH = 175;
private static final int ACTIVITY_TIME_COL_MIN_WIDTH = 250;
private static final int ACTIVITY_TIME_COL_MAX_WIDTH = 450;
private static final int TIME_COL_MIN_WIDTH = 30;
private static final int TIME_COL_MAX_WIDTH = 250;
private static final int TIME_COL_PREFERRED_WIDTH = 140;
private static final int NAME_COL_MIN_WIDTH = 100;
private static final int NAME_COL_MAX_WIDTH = 250;
private static final int NAME_COL_PREFERRED_WIDTH = 140;
private static final int ACTIVITY_COL_MIN_WIDTH = 70;
private static final int ACTIVITY_COL_MAX_WIDTH = 2000;
private static final int ACTIVITY_COL_PREFERRED_WIDTH = 300;
private static final int STATUS_COL_MIN_WIDTH = 55;
private static final int STATUS_COL_MAX_WIDTH = 250;
private static final int STATUS_COL_PREFERRED_WIDTH = 55;
private static final int COMPLETED_TIME_COL_MIN_WIDTH = 30;
private static final int COMPLETED_TIME_COL_MAX_WIDTH = 2000;
private static final int COMPLETED_TIME_COL_PREFERRED_WIDTH = 280;
private static final String UPDATE_TASKS_THREAD_NAME = "AID-update-tasks-%d";
private static final Logger logger = Logger.getLogger(AutoIngestDashboard.class.getName());
private final DefaultTableModel pendingTableModel;
private final DefaultTableModel runningTableModel;
private final DefaultTableModel completedTableModel;
private AutoIngestMonitor autoIngestMonitor;
private ExecutorService updateExecutor;
// DLG: The Viking code needs to be updated, too. See VikingStartupWindow,
// which should be using the AutoIngestControlPanel, not the AutoIngestDashboard.
/**
* Creates a dashboard for monitoring an automated ingest cluster.
*
* @return The dashboard.
*
* @throws AutoIngestDashboardException If there is a problem creating the
* dashboard.
*/
public static AutoIngestDashboard createDashboard() throws AutoIngestDashboardException {
AutoIngestDashboard dashBoard = new AutoIngestDashboard();
try {
dashBoard.startUp();
} catch (AutoIngestMonitor.AutoIngestMonitorException ex) {
throw new AutoIngestDashboardException("Error starting up auto ingest dashboard", ex);
}
return dashBoard;
}
/**
* Constructs a panel for monitoring an automated ingest cluster.
*/
private AutoIngestDashboard() {
pendingTableModel = new DefaultTableModel(JobsTableModelColumns.headers, 0) {
private static final long serialVersionUID = 1L;
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
runningTableModel = new DefaultTableModel(JobsTableModelColumns.headers, 0) {
private static final long serialVersionUID = 1L;
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
completedTableModel = new DefaultTableModel(JobsTableModelColumns.headers, 0) {
private static final long serialVersionUID = 1L;
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
initComponents();
setServicesStatusMessage();
initPendingJobsTable();
initRunningJobsTable();
initCompletedJobsTable();
/*
* Must set this flag, otherwise pop up menus don't close properly.
*/
UIManager.put("PopupMenu.consumeEventOnClose", false);
}
/**
* Queries the services monitor and sets the text for the services status
* text box.
*/
private void setServicesStatusMessage() {
new SwingWorker<Void, Void>() {
String caseDatabaseServerStatus = ServicesMonitor.ServiceStatus.DOWN.toString();
String keywordSearchServiceStatus = ServicesMonitor.ServiceStatus.DOWN.toString();
String messagingStatus = ServicesMonitor.ServiceStatus.DOWN.toString();
@Override
protected Void doInBackground() throws Exception {
caseDatabaseServerStatus = getServiceStatus(ServicesMonitor.Service.REMOTE_CASE_DATABASE);
keywordSearchServiceStatus = getServiceStatus(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH);
messagingStatus = getServiceStatus(ServicesMonitor.Service.MESSAGING);
return null;
}
/**
* Gets a status string for a given service.
*
* @param service The service to test.
*
* @return The status string.
*/
private String getServiceStatus(ServicesMonitor.Service service) {
String serviceStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Unknown");
try {
ServicesMonitor servicesMonitor = ServicesMonitor.getInstance();
serviceStatus = servicesMonitor.getServiceStatus(service.toString());
if (serviceStatus.compareTo(ServicesMonitor.ServiceStatus.UP.toString()) == 0) {
serviceStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Up");
} else {
serviceStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Down");
}
} catch (ServicesMonitor.ServicesMonitorException ex) {
logger.log(Level.SEVERE, String.format("Dashboard error getting service status for %s", service), ex);
}
return serviceStatus;
}
@Override
protected void done() {
tbServicesStatusMessage.setText(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message", caseDatabaseServerStatus, keywordSearchServiceStatus, keywordSearchServiceStatus, messagingStatus));
String upStatus = NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.Message.Up");
if (caseDatabaseServerStatus.compareTo(upStatus) != 0
|| keywordSearchServiceStatus.compareTo(upStatus) != 0
|| messagingStatus.compareTo(upStatus) != 0) {
tbServicesStatusMessage.setForeground(Color.RED);
} else {
tbServicesStatusMessage.setForeground(Color.BLACK);
}
}
}.execute();
}
/**
* Sets up the JTable that presents a view of the pending jobs queue for an
* auto ingest cluster.
*/
private void initPendingJobsTable() {
/*
* Remove some of the jobs table model columns from the JTable. This
* does not remove the columns from the model, just from this table.
*/
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.HOST_NAME.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.STARTED_TIME.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.COMPLETED_TIME.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.STAGE.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.STAGE_TIME.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
/*
* Set up a column to display the cases associated with the jobs.
*/
TableColumn column;
column = pendingTable.getColumn(JobsTableModelColumns.CASE.getColumnHeader());
column.setMinWidth(GENERIC_COL_MIN_WIDTH);
column.setMaxWidth(GENERIC_COL_MAX_WIDTH);
column.setPreferredWidth(PENDING_TABLE_COL_PREFERRED_WIDTH);
column.setWidth(PENDING_TABLE_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the data sources associated with the jobs.
*/
column = pendingTable.getColumn(JobsTableModelColumns.DATA_SOURCE.getColumnHeader());
column.setMaxWidth(GENERIC_COL_MAX_WIDTH);
column.setPreferredWidth(PENDING_TABLE_COL_PREFERRED_WIDTH);
column.setWidth(PENDING_TABLE_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the create times of the jobs.
*/
column = pendingTable.getColumn(JobsTableModelColumns.CREATED_TIME.getColumnHeader());
column.setCellRenderer(new LongDateCellRenderer());
column.setMinWidth(TIME_COL_MIN_WIDTH);
column.setMaxWidth(TIME_COL_MAX_WIDTH);
column.setPreferredWidth(TIME_COL_PREFERRED_WIDTH);
column.setWidth(TIME_COL_PREFERRED_WIDTH);
/**
* Prevent sorting when a column header is clicked.
*/
pendingTable.setAutoCreateRowSorter(false);
/*
* Create a row selection listener to enable/disable the prioritize
* folder and prioritize case buttons.
*/
pendingTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
if (e.getValueIsAdjusting()) {
return;
}
int row = pendingTable.getSelectedRow();
});
}
/**
* Sets up the JTable that presents a view of the running jobs list for an
* auto ingest cluster.
*/
private void initRunningJobsTable() {
/*
* Remove some of the jobs table model columns from the JTable. This
* does not remove the columns from the model, just from this table.
*/
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.CREATED_TIME.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.STARTED_TIME.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.COMPLETED_TIME.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
/*
* Set up a column to display the cases associated with the jobs.
*/
TableColumn column;
column = runningTable.getColumn(JobsTableModelColumns.CASE.getColumnHeader());
column.setMinWidth(GENERIC_COL_MIN_WIDTH);
column.setMaxWidth(GENERIC_COL_MAX_WIDTH);
column.setPreferredWidth(RUNNING_TABLE_COL_PREFERRED_WIDTH);
column.setWidth(RUNNING_TABLE_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the image folders associated with the
* jobs.
*/
column = runningTable.getColumn(JobsTableModelColumns.DATA_SOURCE.getColumnHeader());
column.setMinWidth(GENERIC_COL_MIN_WIDTH);
column.setMaxWidth(GENERIC_COL_MAX_WIDTH);
column.setPreferredWidth(RUNNING_TABLE_COL_PREFERRED_WIDTH);
column.setWidth(RUNNING_TABLE_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the host names of the cluster nodes
* processing the jobs.
*/
column = runningTable.getColumn(JobsTableModelColumns.HOST_NAME.getColumnHeader());
column.setMinWidth(NAME_COL_MIN_WIDTH);
column.setMaxWidth(NAME_COL_MAX_WIDTH);
column.setPreferredWidth(NAME_COL_PREFERRED_WIDTH);
column.setWidth(NAME_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the ingest activities associated with the
* jobs.
*/
column = runningTable.getColumn(JobsTableModelColumns.STAGE.getColumnHeader());
column.setMinWidth(ACTIVITY_COL_MIN_WIDTH);
column.setMaxWidth(ACTIVITY_COL_MAX_WIDTH);
column.setPreferredWidth(ACTIVITY_COL_PREFERRED_WIDTH);
column.setWidth(ACTIVITY_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the ingest activity times associated with
* the jobs.
*/
column = runningTable.getColumn(JobsTableModelColumns.STAGE_TIME.getColumnHeader());
column.setCellRenderer(new DurationCellRenderer());
column.setMinWidth(GENERIC_COL_MIN_WIDTH);
column.setMaxWidth(ACTIVITY_TIME_COL_MAX_WIDTH);
column.setPreferredWidth(ACTIVITY_TIME_COL_MIN_WIDTH);
column.setWidth(ACTIVITY_TIME_COL_MIN_WIDTH);
/*
* Prevent sorting when a column header is clicked.
*/
runningTable.setAutoCreateRowSorter(false);
}
/**
* Sets up the JTable that presents a view of the completed jobs list for an
* auto ingest cluster.
*/
private void initCompletedJobsTable() {
/*
* Remove some of the jobs table model columns from the JTable. This
* does not remove the columns from the model, just from this table.
*/
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.STARTED_TIME.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.STAGE.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.STAGE_TIME.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.HOST_NAME.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
/*
* Set up a column to display the cases associated with the jobs.
*/
TableColumn column;
column = completedTable.getColumn(JobsTableModelColumns.CASE.getColumnHeader());
column.setMinWidth(COMPLETED_TIME_COL_MIN_WIDTH);
column.setMaxWidth(COMPLETED_TIME_COL_MAX_WIDTH);
column.setPreferredWidth(COMPLETED_TIME_COL_PREFERRED_WIDTH);
column.setWidth(COMPLETED_TIME_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the image folders associated with the
* jobs.
*/
column = completedTable.getColumn(JobsTableModelColumns.DATA_SOURCE.getColumnHeader());
column.setMinWidth(COMPLETED_TIME_COL_MIN_WIDTH);
column.setMaxWidth(COMPLETED_TIME_COL_MAX_WIDTH);
column.setPreferredWidth(COMPLETED_TIME_COL_PREFERRED_WIDTH);
column.setWidth(COMPLETED_TIME_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the create times of the jobs.
*/
column = completedTable.getColumn(JobsTableModelColumns.CREATED_TIME.getColumnHeader());
column.setCellRenderer(new LongDateCellRenderer());
column.setMinWidth(TIME_COL_MIN_WIDTH);
column.setMaxWidth(TIME_COL_MAX_WIDTH);
column.setPreferredWidth(TIME_COL_PREFERRED_WIDTH);
column.setWidth(TIME_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the completed times of the jobs.
*/
column = completedTable.getColumn(JobsTableModelColumns.COMPLETED_TIME.getColumnHeader());
column.setCellRenderer(new LongDateCellRenderer());
column.setMinWidth(TIME_COL_MIN_WIDTH);
column.setMaxWidth(TIME_COL_MAX_WIDTH);
column.setPreferredWidth(TIME_COL_PREFERRED_WIDTH);
column.setWidth(TIME_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the statuses of the jobs, with a cell
* renderer that will choose an icon to represent the job status.
*/
column = completedTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader());
column.setCellRenderer(new CaseStatusIconCellRenderer());
column.setMinWidth(STATUS_COL_MIN_WIDTH);
column.setMaxWidth(STATUS_COL_MAX_WIDTH);
column.setPreferredWidth(STATUS_COL_PREFERRED_WIDTH);
column.setWidth(STATUS_COL_PREFERRED_WIDTH);
/*
* Prevent sorting when a column header is clicked.
*/
completedTable.setAutoCreateRowSorter(false);
/*
* Create a row selection listener to enable/disable the delete case and
* show log buttons.
*/
completedTable.getSelectionModel()
.addListSelectionListener((ListSelectionEvent e) -> {
if (e.getValueIsAdjusting()) {
return;
}
int row = completedTable.getSelectedRow();
boolean enabled = row >= 0 && row < completedTable.getRowCount();
});
}
/**
* Starts up the auto ingest monitor and adds this panel as an observer,
* subscribes to services monitor events and starts a task to populate the
* auto ingest job tables.
*/
private void startUp() throws AutoIngestMonitor.AutoIngestMonitorException {
autoIngestMonitor = AutoIngestMonitor.createMonitor();
autoIngestMonitor.addObserver(this);
updateExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(UPDATE_TASKS_THREAD_NAME).build());
updateExecutor.submit(new GetJobsSnapshotTask());
ServicesMonitor.getInstance().addSubscriber((PropertyChangeEvent evt) -> {
setServicesStatusMessage();
});
}
@Override
public void update(Observable observable, Object argument) {
updateExecutor.submit(new GetJobsSnapshotTask());
}
/**
* Reloads the table models using a jobs snapshot and refreshes the JTables
* that use the models.
*
* @param jobsSnapshot The jobs snapshot.
*/
private void refreshTables(JobsSnapshot jobsSnapshot) {
List<AutoIngestJob> pendingJobs = jobsSnapshot.getPendingJobs();
List<AutoIngestJob> runningJobs = jobsSnapshot.getRunningJobs();
List<AutoIngestJob> completedJobs = jobsSnapshot.getCompletedJobs();
// DLG: Do the appropriate sorts.
refreshTable(pendingJobs, pendingTable, pendingTableModel);
refreshTable(runningJobs, runningTable, runningTableModel);
refreshTable(completedJobs, completedTable, completedTableModel);
}
/**
* Reloads the table model for an auto ingest jobs table and refreshes the
* JTable that uses the model.
*
* @param jobs The list of auto ingest jobs.
* @param tableModel The table model.
* @param comparator An optional comparator (may be null) for sorting the
* table model.
*/
private void refreshTable(List<AutoIngestJob> jobs, JTable table, DefaultTableModel tableModel) {
try {
Path currentRow = getSelectedEntry(table, tableModel);
tableModel.setRowCount(0);
for (AutoIngestJob job : jobs) {
if (job.getNodeData().getVersion() < 2) {
// Ignore version '1' nodes since they don't carry enough
// data to populate the table.
continue;
}
AutoIngestJob.StageDetails status = job.getStageDetails();
ManifestNodeData nodeData = job.getNodeData();
tableModel.addRow(new Object[]{
nodeData.getCaseName(), // CASE
nodeData.getDataSourcePath().getFileName(), // DATA_SOURCE
job.getNodeName(), // HOST_NAME
nodeData.getManifestFileDate(), // CREATED_TIME
job.getStageStartDate(), // STARTED_TIME
nodeData.getCompletedDate(), // COMPLETED_TIME
status.getDescription(), // ACTIVITY
nodeData.getErrorsOccurred(), // STATUS
((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime())), // ACTIVITY_TIME
job.getCaseDirectoryPath(), // CASE_DIRECTORY_PATH
nodeData.getManifestFilePath()}); // MANIFEST_FILE_PATH
}
setSelectedEntry(table, tableModel, currentRow);
} catch (Exception ex) {
logger.log(Level.SEVERE, "Error refreshing table " + table.toString(), ex);
}
}
/**
* Gets a path representing the current selection in a table.
*
* @param table The table.
* @param tableModel The table model of the table.
*
* @return A path representing the current selection, or null if there is no
* selection.
*/
Path getSelectedEntry(JTable table, DefaultTableModel tableModel) {
try {
int currentlySelectedRow = table.getSelectedRow();
if (currentlySelectedRow >= 0 && currentlySelectedRow < table.getRowCount()) {
return Paths.get(tableModel.getValueAt(currentlySelectedRow, JobsTableModelColumns.CASE.ordinal()).toString(),
tableModel.getValueAt(currentlySelectedRow, JobsTableModelColumns.DATA_SOURCE.ordinal()).toString());
}
} catch (Exception ignored) {
return null;
}
return null;
}
/**
* Sets the selection of the table to the passed-in path's item, if that
* item exists in the table. If it does not, clears the table selection.
*
* @param table The table.
* @param tableModel The table model of the table.
* @param path The path of the item to set
*/
void setSelectedEntry(JTable table, DefaultTableModel tableModel, Path path) {
if (path != null) {
try {
for (int row = 0; row < table.getRowCount(); ++row) {
Path temp = Paths.get(tableModel.getValueAt(row, JobsTableModelColumns.CASE.ordinal()).toString(),
tableModel.getValueAt(row, JobsTableModelColumns.DATA_SOURCE.ordinal()).toString());
if (temp.compareTo(path) == 0) { // found it
table.setRowSelectionInterval(row, row);
return;
}
}
} catch (Exception ignored) {
table.clearSelection();
}
}
table.clearSelection();
}
/*
* The enum is used in conjunction with the DefaultTableModel class to
* provide table models for the JTables used to display a view of the
* pending jobs queue, running jobs list, and completed jobs list for an
* auto ingest cluster. The enum allows the columns of the table model to be
* described by either an enum ordinal or a column header string.
*/
private enum JobsTableModelColumns {
// DLG: Go through the bundles.properties file and delete and unused key-value pairs.
CASE(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Case")),
DATA_SOURCE(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.ImageFolder")),
HOST_NAME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.HostName")),
CREATED_TIME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.CreatedTime")),
STARTED_TIME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.StartedTime")),
COMPLETED_TIME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.CompletedTime")),
STAGE(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Stage")),
STAGE_TIME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.StageTime")),
STATUS(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Status")),
CASE_DIRECTORY_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.CaseFolder")),
MANIFEST_FILE_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.ManifestFilePath"));
private final String header;
private JobsTableModelColumns(String header) {
this.header = header;
}
private String getColumnHeader() {
return header;
}
private static final String[] headers = {
CASE.getColumnHeader(),
DATA_SOURCE.getColumnHeader(),
HOST_NAME.getColumnHeader(),
CREATED_TIME.getColumnHeader(),
STARTED_TIME.getColumnHeader(),
COMPLETED_TIME.getColumnHeader(),
STAGE.getColumnHeader(),
STATUS.getColumnHeader(),
STAGE_TIME.getColumnHeader(),
CASE_DIRECTORY_PATH.getColumnHeader(),
MANIFEST_FILE_PATH.getColumnHeader()};
}
/**
* A task that gets the current snapshot of the pending, running and
* completed auto ingest jobs lists for an auto ingest cluster from the auto
* ingest monitor, sorts them, and queues a UI components refresh task for
* execution in the EDT.
*/
private class GetJobsSnapshotTask implements Runnable {
@Override
public void run() {
AutoIngestMonitor.JobsSnapshot jobsSnapshot = autoIngestMonitor.getJobsSnapshot();
List<AutoIngestJob> pendingJobs = jobsSnapshot.getPendingJobs();
List<AutoIngestJob> runningJobs = jobsSnapshot.getRunningJobs();
List<AutoIngestJob> completedJobs = jobsSnapshot.getCompletedJobs();
// DLG: Do the appropriate sorts in this background task.
EventQueue.invokeLater(new RefreshComponentsTask(pendingJobs, runningJobs, completedJobs));
}
}
/**
* A task that refreshes the UI components on this panel to reflect a
* snapshot of the pending, running and completed auto ingest jobs lists of
* an auto ingest cluster.
*/
private class RefreshComponentsTask implements Runnable {
private final List<AutoIngestJob> pendingJobs;
private final List<AutoIngestJob> runningJobs;
private final List<AutoIngestJob> completedJobs;
/**
* Constructs a task that refreshes the UI components on this panel to
* reflect a snapshot of the pending, running and completed auto ingest
* jobs lists of an auto ingest cluster.
*
* @param pendingJobs The pending jobs list.
* @param runningJobs The running jobs list.
* @param completedJobs The completed jobs list.
*/
RefreshComponentsTask(List<AutoIngestJob> pendingJobs, List<AutoIngestJob> runningJobs, List<AutoIngestJob> completedJobs) {
this.pendingJobs = pendingJobs;
this.runningJobs = runningJobs;
this.completedJobs = completedJobs;
}
@Override
public void run() {
refreshTable(pendingJobs, pendingTable, pendingTableModel);
refreshTable(runningJobs, runningTable, runningTableModel);
refreshTable(completedJobs, completedTable, completedTableModel);
refreshButton.setEnabled(true);
}
}
/**
* Exception type thrown when there is an error completing an auto ingest
* dashboard operation.
*/
static final class AutoIngestDashboardException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs an instance of the exception type thrown when there is an
* error completing an auto ingest dashboard operation.
*
* @param message The exception message.
*/
private AutoIngestDashboardException(String message) {
super(message);
}
/**
* Constructs an instance of the exception type thrown when there is an
* error completing an auto ingest dashboard operation.
*
* @param message The exception message.
* @param cause A Throwable cause for the error.
*/
private AutoIngestDashboardException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
pendingScrollPane = new javax.swing.JScrollPane();
pendingTable = new javax.swing.JTable();
runningScrollPane = new javax.swing.JScrollPane();
runningTable = new javax.swing.JTable();
completedScrollPane = new javax.swing.JScrollPane();
completedTable = new javax.swing.JTable();
lbPending = new javax.swing.JLabel();
lbRunning = new javax.swing.JLabel();
lbCompleted = new javax.swing.JLabel();
refreshButton = new javax.swing.JButton();
lbServicesStatus = new javax.swing.JLabel();
tbServicesStatusMessage = new javax.swing.JTextField();
prioritizeButton = new javax.swing.JButton();
pendingTable.setModel(pendingTableModel);
pendingTable.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.pendingTable.toolTipText")); // NOI18N
pendingTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_ALL_COLUMNS);
pendingTable.setRowHeight(20);
pendingTable.setSelectionModel(new DefaultListSelectionModel() {
private static final long serialVersionUID = 1L;
@Override
public void setSelectionInterval(int index0, int index1) {
if (index0 == pendingTable.getSelectedRow()) {
pendingTable.clearSelection();
} else {
super.setSelectionInterval(index0, index1);
}
}
});
pendingTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
pendingScrollPane.setViewportView(pendingTable);
runningTable.setModel(runningTableModel);
runningTable.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.runningTable.toolTipText")); // NOI18N
runningTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_ALL_COLUMNS);
runningTable.setRowHeight(20);
runningTable.setSelectionModel(new DefaultListSelectionModel() {
private static final long serialVersionUID = 1L;
@Override
public void setSelectionInterval(int index0, int index1) {
if (index0 == runningTable.getSelectedRow()) {
runningTable.clearSelection();
} else {
super.setSelectionInterval(index0, index1);
}
}
});
runningTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
runningScrollPane.setViewportView(runningTable);
completedTable.setModel(completedTableModel);
completedTable.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.completedTable.toolTipText")); // NOI18N
completedTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_ALL_COLUMNS);
completedTable.setRowHeight(20);
completedTable.setSelectionModel(new DefaultListSelectionModel() {
private static final long serialVersionUID = 1L;
@Override
public void setSelectionInterval(int index0, int index1) {
if (index0 == completedTable.getSelectedRow()) {
completedTable.clearSelection();
} else {
super.setSelectionInterval(index0, index1);
}
}
});
completedTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
completedScrollPane.setViewportView(completedTable);
lbPending.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbPending, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.lbPending.text")); // NOI18N
lbRunning.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbRunning, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.lbRunning.text")); // NOI18N
lbCompleted.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbCompleted, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.lbCompleted.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.refreshButton.text")); // NOI18N
refreshButton.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.refreshButton.toolTipText")); // NOI18N
refreshButton.setEnabled(false);
refreshButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
refreshButtonActionPerformed(evt);
}
});
lbServicesStatus.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbServicesStatus, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.lbServicesStatus.text")); // NOI18N
tbServicesStatusMessage.setEditable(false);
tbServicesStatusMessage.setFont(new java.awt.Font("Tahoma", 1, 12)); // NOI18N
tbServicesStatusMessage.setText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.text")); // NOI18N
tbServicesStatusMessage.setBorder(null);
org.openide.awt.Mnemonics.setLocalizedText(prioritizeButton, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.prioritizeButton.text")); // NOI18N
prioritizeButton.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.prioritizeButton.toolTipText")); // NOI18N
prioritizeButton.setEnabled(false);
prioritizeButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
prioritizeButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lbPending)
.addComponent(lbCompleted)
.addComponent(lbRunning)
.addGroup(layout.createSequentialGroup()
.addComponent(lbServicesStatus)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(tbServicesStatusMessage, javax.swing.GroupLayout.PREFERRED_SIZE, 861, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addComponent(refreshButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(prioritizeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(pendingScrollPane, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(runningScrollPane)
.addComponent(completedScrollPane))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbServicesStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(tbServicesStatusMessage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lbPending, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pendingScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lbRunning)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(runningScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lbCompleted)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(completedScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 179, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(refreshButton)
.addComponent(prioritizeButton))
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
/**
* Handles a click on the Refresh button. Requests a refreshed jobs snapshot
* from the auto ingest monitor and uses it to refresh the UI components of
* the panel.
*
* @param evt The button click event.
*/
private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshButtonActionPerformed
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
JobsSnapshot jobsSnapshot = autoIngestMonitor.refreshJobsSnapshot();
refreshTables(jobsSnapshot);
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}//GEN-LAST:event_refreshButtonActionPerformed
private void prioritizeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prioritizeButtonActionPerformed
if (pendingTableModel.getRowCount() > 0 && pendingTable.getSelectedRow() >= 0) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Path manifestFilePath = (Path) (pendingTableModel.getValueAt(pendingTable.getSelectedRow(), JobsTableModelColumns.MANIFEST_FILE_PATH.ordinal()));
JobsSnapshot jobsSnapshot;
try {
jobsSnapshot = autoIngestMonitor.prioritizeJob(manifestFilePath);
refreshTables(jobsSnapshot);
} catch (AutoIngestMonitor.AutoIngestMonitorException ex) {
// DLG: Log the exception and do a popup with a user-friendly
// message explaining that the operation failed
}
setCursor(Cursor.getDefaultCursor());
}
}//GEN-LAST:event_prioritizeButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane completedScrollPane;
private javax.swing.JTable completedTable;
private javax.swing.JLabel lbCompleted;
private javax.swing.JLabel lbPending;
private javax.swing.JLabel lbRunning;
private javax.swing.JLabel lbServicesStatus;
private javax.swing.JScrollPane pendingScrollPane;
private javax.swing.JTable pendingTable;
private javax.swing.JButton prioritizeButton;
private javax.swing.JButton refreshButton;
private javax.swing.JScrollPane runningScrollPane;
private javax.swing.JTable runningTable;
private javax.swing.JTextField tbServicesStatusMessage;
// End of variables declaration//GEN-END:variables
}