19 package org.sleuthkit.autopsy.healthmonitor;
 
   21 import java.awt.Container;
 
   22 import java.awt.Cursor;
 
   23 import java.awt.Dimension;
 
   25 import java.util.HashSet;
 
   26 import java.util.HashMap;
 
   28 import java.util.Arrays;
 
   29 import java.util.ArrayList;
 
   30 import java.util.List;
 
   31 import java.awt.event.ActionEvent;
 
   32 import java.awt.event.ActionListener;
 
   33 import java.io.BufferedWriter;
 
   35 import java.io.FileOutputStream;
 
   36 import java.io.IOException;
 
   37 import java.io.OutputStreamWriter;
 
   38 import javax.swing.Box;
 
   39 import javax.swing.JButton;
 
   40 import javax.swing.JDialog;
 
   41 import javax.swing.JComboBox;
 
   42 import javax.swing.JSeparator;
 
   43 import javax.swing.JCheckBox;
 
   44 import javax.swing.JLabel;
 
   45 import javax.swing.JPanel;
 
   46 import javax.swing.JScrollPane;
 
   47 import javax.swing.BorderFactory;
 
   48 import javax.swing.BoxLayout;
 
   49 import java.awt.GridLayout;
 
   50 import java.nio.charset.StandardCharsets;
 
   51 import java.nio.file.Paths;
 
   52 import java.text.DateFormat;
 
   53 import java.text.SimpleDateFormat;
 
   54 import java.util.Date;
 
   55 import java.util.Collections;
 
   56 import java.util.logging.Level;
 
   57 import java.util.stream.Collectors;
 
   58 import javax.swing.JFileChooser;
 
   59 import org.openide.util.NbBundle;
 
   98         timingData = 
new HashMap<>();
 
   99         userData = 
new ArrayList<>();
 
  100         parentWindow = parent;
 
  107     @NbBundle.Messages({
"HealthMonitorDashboard.display.errorCreatingDashboard=Error creating health monitor dashboard",
 
  108                         "HealthMonitorDashboard.display.dashboardTitle=Health Monitor"})
 
  121         } 
catch (HealthMonitorException ex) {
 
  122             logger.log(Level.SEVERE, 
"Error creating panels for health monitor dashboard", ex);
 
  128         JPanel mainPanel = 
new JPanel();
 
  129         mainPanel.setLayout(
new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
 
  132         mainPanel.add(timingPanel);
 
  135         mainPanel.add(userPanel);
 
  138         File adminFile = 
new File(ADMIN_ACCESS_FILE_PATH);
 
  139         if(adminFile.exists()) {
 
  140             mainPanel.add(adminPanel);
 
  144         dialog = 
new JDialog();
 
  145         dialog.setTitle(Bundle.HealthMonitorDashboard_display_dashboardTitle());
 
  146         dialog.add(mainPanel);
 
  148         dialog.setLocationRelativeTo(parentWindow);
 
  149         dialog.setVisible(
true);
 
  157         if (dialog != null) {
 
  158             dialog.setVisible(
false);
 
  187     @NbBundle.Messages({
"HealthMonitorDashboard.createTimingPanel.noData=No data to display - monitor is not enabled",
 
  188                         "HealthMonitorDashboard.createTimingPanel.timingMetricsTitle=Timing Metrics"})
 
  194             JPanel emptyTimingMetricPanel = 
new JPanel();
 
  195             emptyTimingMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createTimingPanel_timingMetricsTitle()));
 
  196             emptyTimingMetricPanel.add(
new JLabel(
" "));
 
  197             emptyTimingMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createTimingPanel_noData()));
 
  199             return emptyTimingMetricPanel;
 
  202         JPanel timingMetricPanel = 
new JPanel();
 
  203         timingMetricPanel.setLayout(
new BoxLayout(timingMetricPanel, BoxLayout.PAGE_AXIS));
 
  204         timingMetricPanel.setBorder(BorderFactory.createEtchedBorder());
 
  207         JLabel timingMetricTitle = 
new JLabel(Bundle.HealthMonitorDashboard_createTimingPanel_timingMetricsTitle());
 
  208         timingMetricPanel.add(timingMetricTitle);
 
  209         timingMetricPanel.add(
new JSeparator());   
 
  213         timingMetricPanel.add(
new JSeparator());
 
  216         timingGraphPanel = 
new JPanel();
 
  217         timingGraphPanel.setLayout(
new GridLayout(0,2));
 
  221         JScrollPane scrollPane = 
new JScrollPane(timingGraphPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
 
  222         timingMetricPanel.add(scrollPane);
 
  223         timingMetricPanel.revalidate();
 
  224         timingMetricPanel.repaint();
 
  226         return timingMetricPanel;
 
  233     @NbBundle.Messages({
"HealthMonitorDashboard.createTimingControlPanel.filterByHost=Filter by host",
 
  234                         "HealthMonitorDashboard.createTimingControlPanel.maxDays=Max days to display",
 
  235                         "HealthMonitorDashboard.createTimingControlPanel.skipOutliers=Do not plot outliers",
 
  236                         "HealthMonitorDashboard.createTimingControlPanel.showTrendLine=Show trend line"})
 
  238         JPanel timingControlPanel = 
new JPanel();
 
  242             return timingControlPanel;
 
  246         String[] dateOptionStrings = Arrays.stream(
DateRange.values()).map(e -> e.getLabel()).toArray(String[]::
new);
 
  247         timingDateComboBox = 
new JComboBox<>(dateOptionStrings);
 
  251         timingDateComboBox.addActionListener(
new ActionListener() {
 
  253             public void actionPerformed(ActionEvent arg0) {
 
  256                 } 
catch (HealthMonitorException ex) {
 
  257                     logger.log(Level.SEVERE, 
"Error updating timing metric panel", ex);
 
  263         Set<String> hostNameSet = 
new HashSet<>();
 
  264         for(String metricType:timingData.keySet()) {
 
  266                 hostNameSet.add(result.getHostName());
 
  271         timingHostComboBox = 
new JComboBox<>(hostNameSet.toArray(
new String[hostNameSet.size()]));
 
  274         timingHostComboBox.addActionListener(
new ActionListener() {
 
  276             public void actionPerformed(ActionEvent arg0) {
 
  278                     if((timingHostCheckBox != null) && timingHostCheckBox.isSelected()) {
 
  281                 } 
catch (HealthMonitorException ex) {
 
  282                     logger.log(Level.SEVERE, 
"Error populating timing metric panel", ex);
 
  288         timingHostCheckBox = 
new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_filterByHost());
 
  289         timingHostCheckBox.setSelected(
false);
 
  290         timingHostComboBox.setEnabled(
false);
 
  293         timingHostCheckBox.addActionListener(
new ActionListener() {
 
  295             public void actionPerformed(ActionEvent arg0) {
 
  297                     timingHostComboBox.setEnabled(timingHostCheckBox.isSelected());
 
  299                 } 
catch (HealthMonitorException ex) {
 
  300                     logger.log(Level.SEVERE, 
"Error populating timing metric panel", ex);
 
  306         timingShowTrendLineCheckBox = 
new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_showTrendLine());
 
  307         timingShowTrendLineCheckBox.setSelected(
true);
 
  310         timingShowTrendLineCheckBox.addActionListener(
new ActionListener() {
 
  312             public void actionPerformed(ActionEvent arg0) {
 
  315                 } 
catch (HealthMonitorException ex) {
 
  316                     logger.log(Level.SEVERE, 
"Error populating timing metric panel", ex);
 
  322         timingSkipOutliersCheckBox = 
new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_skipOutliers());
 
  323         timingSkipOutliersCheckBox.setSelected(
false);
 
  326         timingSkipOutliersCheckBox.addActionListener(
new ActionListener() {
 
  328             public void actionPerformed(ActionEvent arg0) {
 
  331                 } 
catch (HealthMonitorException ex) {
 
  332                     logger.log(Level.SEVERE, 
"Error populating timing metric panel", ex);
 
  338         timingControlPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createTimingControlPanel_maxDays()));
 
  339         timingControlPanel.add(timingDateComboBox);
 
  342         timingControlPanel.add(Box.createHorizontalStrut(100));
 
  345         timingControlPanel.add(timingHostCheckBox);
 
  346         timingControlPanel.add(timingHostComboBox);
 
  349         timingControlPanel.add(Box.createHorizontalStrut(100));
 
  352         timingControlPanel.add(this.timingShowTrendLineCheckBox);
 
  355         timingControlPanel.add(Box.createHorizontalStrut(100));
 
  358         timingControlPanel.add(this.timingSkipOutliersCheckBox);
 
  360         return timingControlPanel;
 
  367     @NbBundle.Messages({
"HealthMonitorDashboard.updateTimingMetricGraphs.noData=No data to display"})
 
  371         timingGraphPanel.removeAll();
 
  373         if(timingData.keySet().isEmpty()) {
 
  375             timingGraphPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_updateTimingMetricGraphs_noData()));
 
  379         for(String metricName:timingData.keySet()) {
 
  383             if(timingDateComboBox.getSelectedItem() != null) {
 
  385                 long threshold = System.currentTimeMillis() - selectedDateRange.
getTimestampRange();
 
  386                 intermediateTimingDataForDisplay = timingData.get(metricName).stream()
 
  387                         .filter(t -> t.getTimestamp() > threshold)
 
  388                         .collect(Collectors.toList());
 
  390                 intermediateTimingDataForDisplay = timingData.get(metricName);
 
  396             String hostToDisplay = null;
 
  397             if(timingHostCheckBox.isSelected() && (timingHostComboBox.getSelectedItem() != null)) {
 
  398                 hostToDisplay = timingHostComboBox.getSelectedItem().toString();
 
  402             TimingMetricGraphPanel singleTimingGraphPanel = 
new TimingMetricGraphPanel(intermediateTimingDataForDisplay, 
 
  403                     hostToDisplay, 
true, metricName, timingSkipOutliersCheckBox.isSelected(), timingShowTrendLineCheckBox.isSelected());
 
  404             singleTimingGraphPanel.setPreferredSize(
new Dimension(700,200));
 
  406             timingGraphPanel.add(singleTimingGraphPanel);
 
  408         timingGraphPanel.revalidate();
 
  409         timingGraphPanel.repaint();
 
  417     @NbBundle.Messages({
"HealthMonitorDashboard.createUserPanel.noData=No data to display - monitor is not enabled",
 
  418                     "HealthMonitorDashboard.createUserPanel.userMetricsTitle=User Metrics"})
 
  422             JPanel emptyUserMetricPanel = 
new JPanel();
 
  423             emptyUserMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_userMetricsTitle()));
 
  424             emptyUserMetricPanel.add(
new JLabel(
" "));
 
  425             emptyUserMetricPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_noData()));
 
  427             return emptyUserMetricPanel;
 
  430         JPanel userMetricPanel = 
new JPanel();
 
  431         userMetricPanel.setLayout(
new BoxLayout(userMetricPanel, BoxLayout.PAGE_AXIS));
 
  432         userMetricPanel.setBorder(BorderFactory.createEtchedBorder());
 
  435         JLabel userMetricTitle = 
new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_userMetricsTitle());
 
  436         userMetricPanel.add(userMetricTitle);
 
  437         userMetricPanel.add(
new JSeparator());  
 
  441         userMetricPanel.add(
new JSeparator());
 
  444         userGraphPanel = 
new JPanel();
 
  445         userGraphPanel.setLayout(
new GridLayout(0,2));
 
  449         JScrollPane scrollPane = 
new JScrollPane(userGraphPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
 
  450         userMetricPanel.add(scrollPane);
 
  451         userMetricPanel.revalidate();
 
  452         userMetricPanel.repaint();
 
  454         return userMetricPanel;
 
  461     @NbBundle.Messages({
"HealthMonitorDashboard.createUserControlPanel.maxDays=Max days to display",
 
  462         "HealthMonitorDashboard.createUserControlPanel.userReportButton=Generate Report",
 
  463         "HealthMonitorDashboard.createUserControlPanel.reportError=Error generating report",
 
  464         "# {0} - Report file name",
 
  465         "HealthMonitorDashboard.createUserControlPanel.reportDone=Report saved to: {0}"})
 
  467         JPanel userControlPanel = 
new JPanel();
 
  471             return userControlPanel;
 
  475         String[] dateOptionStrings = Arrays.stream(
DateRange.values()).map(e -> e.getLabel()).toArray(String[]::
new);
 
  476         userDateComboBox = 
new JComboBox<>(dateOptionStrings);
 
  480         userDateComboBox.addActionListener(
new ActionListener() {
 
  482             public void actionPerformed(ActionEvent arg0) {
 
  485                 } 
catch (HealthMonitorException ex) {
 
  486                     logger.log(Level.SEVERE, 
"Error updating user metric panel", ex);
 
  492         userControlPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_createUserControlPanel_maxDays()));
 
  493         userControlPanel.add(userDateComboBox);
 
  496         JButton reportButton = 
new JButton(Bundle.HealthMonitorDashboard_createUserControlPanel_userReportButton());
 
  499         reportButton.addActionListener(
new ActionListener() {
 
  501             public void actionPerformed(ActionEvent arg0) {
 
  502                 JFileChooser reportFileChooser = chooserHelper.
getChooser();
 
  503                 reportFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
 
  505                 final DateFormat csvTimestampFormat = 
new SimpleDateFormat(
"yyyyMMdd_HHmmss");
 
  506                 String fileName = 
"UserReport_" + csvTimestampFormat.format(
new Date())+ 
".csv";
 
  507                 reportFileChooser.setSelectedFile(
new File(fileName));
 
  509                 int returnVal = reportFileChooser.showSaveDialog(userControlPanel);
 
  510                 if (returnVal == JFileChooser.APPROVE_OPTION) {
 
  512                     File selectedFile = reportFileChooser.getSelectedFile();
 
  515                         dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  518                     } 
catch (HealthMonitorException ex) {
 
  519                         logger.log(Level.SEVERE, 
"Error generating report", ex);
 
  522                         dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 
  527         userControlPanel.add(reportButton);
 
  529         return userControlPanel;
 
  540         final DateFormat timestampFormat = 
new SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss");
 
  542         try (BufferedWriter writer = 
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(reportFile), StandardCharsets.UTF_8))) {
 
  544             writer.write(
"Case open,Case close,Duration,Host,User,Case name");
 
  549             Collections.sort(dataForReport);
 
  553             for (
int caseOpenIndex = 0; caseOpenIndex < dataForReport.size() - 1; caseOpenIndex++) {
 
  562                 for (
int nextEventIndex = caseOpenIndex + 1; nextEventIndex < dataForReport.size(); nextEventIndex++) {
 
  565                     if ( nextEvent.getHostname().equals(caseOpenEvent.getHostname())
 
  566                         && nextEvent.getUserName().equals(caseOpenEvent.getUserName())) {
 
  567                         nextEventAfterCaseOpen = nextEvent;
 
  573                 String caseOpenTime = timestampFormat.format(caseOpenEvent.getTimestamp());
 
  581                 String caseCloseTime = 
"";
 
  582                 String duration = 
"";
 
  583                 if (nextEventAfterCaseOpen != null
 
  585                         && nextEventAfterCaseOpen.getCaseName().equals(caseOpenEvent.getCaseName())) {
 
  586                     caseCloseTime = timestampFormat.format(nextEventAfterCaseOpen.getTimestamp());
 
  587                     duration = 
getDuration(caseOpenEvent.getTimestamp(), nextEventAfterCaseOpen.getTimestamp());
 
  590                 String host = caseOpenEvent.getHostname();
 
  591                 String user = caseOpenEvent.getUserName();
 
  592                 String caseName = caseOpenEvent.getCaseName();
 
  594                 String csvEntry = caseOpenTime + 
"," + caseCloseTime + 
"," + duration + 
"," + host + 
"," + user + 
",\"" + caseName + 
"\"";
 
  595                 writer.write(csvEntry);
 
  598         } 
catch (IOException ex) {
 
  599             throw new HealthMonitorException(
"Error writing to output file " + reportFile.getAbsolutePath(), ex);
 
  613         long durationInSeconds = (end - start) / 1000;
 
  614         long second = durationInSeconds % 60;
 
  615         long minute = (durationInSeconds / 60) % 60;
 
  616         long hours = durationInSeconds / (60 * 60);
 
  618         return String.format(
"%d:%02d:%02d", hours, minute, second);
 
  625     @NbBundle.Messages({
"HealthMonitorDashboard.updateUserMetricGraphs.noData=No data to display"})
 
  629         userGraphPanel.removeAll();
 
  631         if(userData.isEmpty()) {
 
  633             userGraphPanel.add(
new JLabel(Bundle.HealthMonitorDashboard_updateUserMetricGraphs_noData()));
 
  642         long timestampThreshold;
 
  643         if(userDateComboBox.getSelectedItem() != null) {
 
  645             timestampThreshold = System.currentTimeMillis() - selectedDateRange.
getTimestampRange();
 
  652         UserMetricGraphPanel caseGraphPanel = 
new UserMetricGraphPanel(userData, timestampThreshold, 
true);
 
  653         caseGraphPanel.setPreferredSize(
new Dimension(700,200));
 
  655         UserMetricGraphPanel logonGraphPanel = 
new UserMetricGraphPanel(userData, timestampThreshold, 
false);
 
  656         logonGraphPanel.setPreferredSize(
new Dimension(700,200));
 
  658         userGraphPanel.add(caseGraphPanel);
 
  659         userGraphPanel.add(logonGraphPanel);
 
  660         userGraphPanel.revalidate();
 
  661         userGraphPanel.repaint();
 
  669     @NbBundle.Messages({
"HealthMonitorDashboard.createAdminPanel.enableButton=Enable monitor",
 
  670                         "HealthMonitorDashboard.createAdminPanel.disableButton=Disable monitor"})
 
  673         JPanel adminPanel = 
new JPanel();
 
  674         adminPanel.setBorder(BorderFactory.createEtchedBorder());
 
  677         JButton enableButton = 
new JButton(Bundle.HealthMonitorDashboard_createAdminPanel_enableButton());
 
  678         JButton disableButton = 
new JButton(Bundle.HealthMonitorDashboard_createAdminPanel_disableButton());
 
  681         enableButton.setEnabled(! isEnabled);
 
  682         disableButton.setEnabled(isEnabled);
 
  685         enableButton.addActionListener(
new ActionListener() {
 
  687             public void actionPerformed(ActionEvent arg0) {
 
  689                     dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  692                 } 
catch (HealthMonitorException ex) {
 
  693                     logger.log(Level.SEVERE, 
"Error enabling monitoring", ex);
 
  695                     dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 
  701         disableButton.addActionListener(
new ActionListener() {
 
  703             public void actionPerformed(ActionEvent arg0) {
 
  705                     dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  708                 } 
catch (HealthMonitorException ex) {
 
  709                     logger.log(Level.SEVERE, 
"Error disabling monitoring", ex);
 
  711                     dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 
  717         adminPanel.add(enableButton);
 
  718         adminPanel.add(Box.createHorizontalStrut(25));
 
  719         adminPanel.add(disableButton);
 
  727     @NbBundle.Messages({
"HealthMonitorDashboard.DateRange.oneMonth=One month",
 
  728                         "HealthMonitorDashboard.DateRange.twoWeeks=Two weeks",
 
  729                         "HealthMonitorDashboard.DateRange.oneWeek=One week",
 
  730                         "HealthMonitorDashboard.DateRange.oneDay=One day"})
 
  732         ONE_DAY(Bundle.HealthMonitorDashboard_DateRange_oneDay(), 1),
 
  733         ONE_WEEK(Bundle.HealthMonitorDashboard_DateRange_oneWeek(), 7),
 
  734         TWO_WEEKS(Bundle.HealthMonitorDashboard_DateRange_twoWeeks(), 14),
 
  735         ONE_MONTH(Bundle.HealthMonitorDashboard_DateRange_oneMonth(), 31);
 
  739         private static final long MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24;
 
  743             this.numberOfDays = numberOfDays;
 
  761             if (numberOfDays > 0) {
 
  762                 return numberOfDays * MILLISECONDS_PER_DAY;
 
  764                 return Long.MAX_VALUE;
 
  774             long maxRange = Long.MIN_VALUE;
 
  776                 if (dateRange.getTimestampRange() > maxRange) {
 
  777                     maxRange = dateRange.getTimestampRange();
 
  785                 if (dateRange.label.equalsIgnoreCase(text)) {
 
JPanel createTimingPanel()
 
JCheckBox timingSkipOutliersCheckBox
 
final Container parentWindow
 
static DateRange fromLabel(String text)
 
HealthMonitorDashboard(Container parent)
 
JPanel createTimingControlPanel()
 
static final String ADMIN_ACCESS_FILE_NAME
 
static void setHealthMonitorReportPath(String reportPath)
 
JPanel createAdminPanel()
 
JCheckBox timingShowTrendLineCheckBox
 
DateRange(String label, long numberOfDays)
 
void updateTimingMetricGraphs()
 
JCheckBox timingHostCheckBox
 
static final String ADMIN_ACCESS_FILE_PATH
 
JPanel createUserControlPanel()
 
static String getHealthMonitorReportPath()
 
JComboBox< String > timingHostComboBox
 
JFileChooser getChooser()
 
JComboBox< String > timingDateComboBox
 
final JFileChooserFactory chooserHelper
 
JComboBox< String > userDateComboBox
 
void updateUserMetricGraphs()
 
synchronized static Logger getLogger(String name)
 
static final Logger logger
 
static long getMaximumTimestampRange()
 
void generateCSVUserReport(File reportFile)
 
static void info(String message)
 
String getDuration(long start, long end)
 
static void error(String message)