mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-15 09:17:42 +00:00
Merge remote-tracking branch 'upstream/custom-release-may-2018' into 3613_dashboard
# Conflicts: # Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java
This commit is contained in:
commit
08878ed407
22
.travis.yml
Normal file
22
.travis.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
language: java
|
||||||
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- TSK_HOME=$TRAVIS_BUILD_DIR/sleuthkit/sleuthkit
|
||||||
|
jdk:
|
||||||
|
- oraclejdk8
|
||||||
|
before_install:
|
||||||
|
- git clone https://github.com/sleuthkit/sleuthkit.git sleuthkit/sleuthkit
|
||||||
|
install:
|
||||||
|
- sudo apt-get install testdisk
|
||||||
|
- cd sleuthkit/sleuthkit
|
||||||
|
- sh install-sleuthkit.sh
|
||||||
|
script:
|
||||||
|
- cd $TRAVIS_BUILD_DIR/
|
||||||
|
- ant build
|
||||||
|
- cd Core/
|
||||||
|
- xvfb-run ant test
|
||||||
|
|
@ -12,7 +12,7 @@
|
|||||||
<property name="thirdparty.dir" value="${basedir}/../thirdparty" />
|
<property name="thirdparty.dir" value="${basedir}/../thirdparty" />
|
||||||
<property name="modules.dir" value="${basedir}/release/modules/" />
|
<property name="modules.dir" value="${basedir}/release/modules/" />
|
||||||
<property name="ext.dir" value="${modules.dir}/ext" />
|
<property name="ext.dir" value="${modules.dir}/ext" />
|
||||||
<property name="test-input" location="test"/>
|
<property name="test-input" location="test/qa-functional/data"/>
|
||||||
|
|
||||||
<target name="get-InternalPythonModules" description="get internal python modules">
|
<target name="get-InternalPythonModules" description="get internal python modules">
|
||||||
<copy todir="release/InternalPythonModules" >
|
<copy todir="release/InternalPythonModules" >
|
||||||
@ -83,6 +83,7 @@
|
|||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="getImageFile">
|
<target name="getImageFile">
|
||||||
|
<mkdir dir="${basedir}/test/qa-functional/data"/>
|
||||||
<get src="https://drive.google.com/uc?id=0BxdBkzm5VKGNT0dGY0dqcHVsU3M" dest="${test-input}/filter_test1.img" skipexisting="true"/>
|
<get src="https://drive.google.com/uc?id=0BxdBkzm5VKGNT0dGY0dqcHVsU3M" dest="${test-input}/filter_test1.img" skipexisting="true"/>
|
||||||
</target>
|
</target>
|
||||||
<target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist, getImageFile">
|
<target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist, getImageFile">
|
||||||
|
@ -176,9 +176,15 @@ public abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
* @returns New Case class with populated database ID
|
* @returns New Case class with populated database ID
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException {
|
public synchronized CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException {
|
||||||
Connection conn = connect();
|
|
||||||
|
// check if there is already an existing CorrelationCase for this Case
|
||||||
|
CorrelationCase cRCase = getCaseByUUID(eamCase.getCaseUUID());
|
||||||
|
if (cRCase != null) {
|
||||||
|
return cRCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection conn = connect();
|
||||||
PreparedStatement preparedStatement = null;
|
PreparedStatement preparedStatement = null;
|
||||||
|
|
||||||
String sql = "INSERT INTO cases(case_uid, org_id, case_name, creation_date, case_number, "
|
String sql = "INSERT INTO cases(case_uid, org_id, case_name, creation_date, case_number, "
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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.communications;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import org.openide.util.Utilities;
|
||||||
|
import org.openide.util.actions.Presenter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for actions that act on the selected AccountDeviceInstanceKeys.
|
||||||
|
* getPopupPresenter() provides a JMenuItem that works (i.e., has an icon) in
|
||||||
|
* custom context menus and also in the Netbeans Explorer views.
|
||||||
|
*/
|
||||||
|
abstract class AbstractCVTAction extends AbstractAction implements Presenter.Popup {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selected accounts that will be acted upon.
|
||||||
|
*
|
||||||
|
* @return The selected accounts
|
||||||
|
*/
|
||||||
|
Collection<? extends AccountDeviceInstanceKey> getSelectedAccounts() {
|
||||||
|
return Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JMenuItem getPopupPresenter() {
|
||||||
|
JMenuItem presenter = new JMenuItem(this);
|
||||||
|
presenter.setText(getActionDisplayName());
|
||||||
|
presenter.setIcon(getIcon());
|
||||||
|
return presenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name/text of the action as displayed in a menu.
|
||||||
|
*
|
||||||
|
* @return The diaplay name of this action
|
||||||
|
*/
|
||||||
|
abstract String getActionDisplayName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The icon to use for this icon.
|
||||||
|
*
|
||||||
|
* @return An ImageIcon used to represent this action.
|
||||||
|
*/
|
||||||
|
abstract ImageIcon getIcon();
|
||||||
|
}
|
@ -18,17 +18,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.communications;
|
package org.sleuthkit.autopsy.communications;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import org.openide.nodes.AbstractNode;
|
import org.openide.nodes.AbstractNode;
|
||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.Utilities;
|
|
||||||
import org.openide.util.lookup.Lookups;
|
import org.openide.util.lookup.Lookups;
|
||||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||||
import org.sleuthkit.datamodel.Account;
|
import org.sleuthkit.datamodel.Account;
|
||||||
@ -102,58 +98,8 @@ final class AccountDeviceInstanceNode extends AbstractNode {
|
|||||||
@Override
|
@Override
|
||||||
public Action[] getActions(boolean context) {
|
public Action[] getActions(boolean context) {
|
||||||
ArrayList<Action> actions = new ArrayList<>(Arrays.asList(super.getActions(context)));
|
ArrayList<Action> actions = new ArrayList<>(Arrays.asList(super.getActions(context)));
|
||||||
// actions.add(PinAccountsAction.getInstance());
|
actions.add(PinAccountsAction.getInstance());
|
||||||
// actions.add(ResetAndPinAccountsAction.getInstance());
|
actions.add(ResetAndPinAccountsAction.getInstance());
|
||||||
return actions.toArray(new Action[actions.size()]);
|
return actions.toArray(new Action[actions.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Action that pins the selected AccountDeviceInstances to the
|
|
||||||
* visualization.
|
|
||||||
*/
|
|
||||||
static private class PinAccountsAction extends AbstractAction {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private final static PinAccountsAction instance = new PinAccountsAction();
|
|
||||||
|
|
||||||
private static PinAccountsAction getInstance() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PinAccountsAction() {
|
|
||||||
super("Add Account(s) to Visualization");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
Collection<? extends AccountDeviceInstanceKey> lookupAll =
|
|
||||||
Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class);
|
|
||||||
CVTEvents.getCVTEventBus().post(new CVTEvents.PinAccountsEvent(lookupAll, false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Action that pins the selected AccountDeviceInstances to the
|
|
||||||
* visualization.
|
|
||||||
*/
|
|
||||||
static private class ResetAndPinAccountsAction extends AbstractAction {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private final static ResetAndPinAccountsAction instance = new ResetAndPinAccountsAction();
|
|
||||||
|
|
||||||
private static ResetAndPinAccountsAction getInstance() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResetAndPinAccountsAction() {
|
|
||||||
super("Visualize Account(s)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
Collection<? extends AccountDeviceInstanceKey> lookupAll =
|
|
||||||
Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class);
|
|
||||||
CVTEvents.getCVTEventBus().post(new CVTEvents.PinAccountsEvent(lookupAll, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,6 @@ CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize
|
|||||||
CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse
|
CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse
|
||||||
CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualize
|
CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualize
|
||||||
CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize
|
CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize
|
||||||
VisualizationPanel.jButton6.text=Hierarchy
|
|
||||||
VisualizationPanel.jButton7.text=Circle
|
|
||||||
VisualizationPanel.jButton8.text=Organic
|
|
||||||
VisualizationPanel.fitGraphButton.text=
|
VisualizationPanel.fitGraphButton.text=
|
||||||
VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin.
|
VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin.
|
||||||
VisualizationPanel.jLabel1.text=Layouts:
|
VisualizationPanel.jLabel1.text=Layouts:
|
||||||
@ -36,11 +33,8 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in
|
|||||||
VisualizationPanel.zoomInButton.text=
|
VisualizationPanel.zoomInButton.text=
|
||||||
VisualizationPanel.zoomOutButton.toolTipText=Zoom out
|
VisualizationPanel.zoomOutButton.toolTipText=Zoom out
|
||||||
VisualizationPanel.zoomOutButton.text=
|
VisualizationPanel.zoomOutButton.text=
|
||||||
# To change this license header, choose License Headers in Project Properties.
|
|
||||||
# To change this template file, choose Tools | Templates
|
|
||||||
# and open the template in the editor.
|
|
||||||
VisualizationPanel.circleLayoutButton.text=Circle
|
VisualizationPanel.circleLayoutButton.text=Circle
|
||||||
VisualizationPanel.organicLayoutButton.text=Organic
|
VisualizationPanel.organicLayoutButton.text=Organic
|
||||||
VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic
|
VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic
|
||||||
VisualizationPanel.hierarchyLayoutButton.text=Hierarchy
|
VisualizationPanel.hierarchyLayoutButton.text=Hierarchical
|
||||||
VisualizationPanel.clearVizButton.text_1=Clear Viz.
|
VisualizationPanel.clearVizButton.text_1=Clear Viz.
|
||||||
|
@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.communications;
|
|||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
|
||||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,7 +78,7 @@ final class CVTEvents {
|
|||||||
return accountDeviceInstances;
|
return accountDeviceInstances;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnpinAccountsEvent(Set<AccountDeviceInstanceKey> accountDeviceInstances) {
|
UnpinAccountsEvent(Collection<? extends AccountDeviceInstanceKey> accountDeviceInstances) {
|
||||||
this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances);
|
this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||||
<NonVisualComponents>
|
|
||||||
<Component class="org.sleuthkit.autopsy.communications.VisualizationPanel" name="vizPanel">
|
|
||||||
</Component>
|
|
||||||
</NonVisualComponents>
|
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||||
@ -72,6 +68,20 @@
|
|||||||
</Constraint>
|
</Constraint>
|
||||||
</Constraints>
|
</Constraints>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component class="org.sleuthkit.autopsy.communications.VisualizationPanel" name="vizPanel">
|
||||||
|
<Constraints>
|
||||||
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
|
||||||
|
<JTabbedPaneConstraints tabName="Visualize">
|
||||||
|
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="CVTTopComponent.vizPanel.TabConstraints.tabTitle_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="tabIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/emblem-web.png"/>
|
||||||
|
</Property>
|
||||||
|
</JTabbedPaneConstraints>
|
||||||
|
</Constraint>
|
||||||
|
</Constraints>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Container>
|
</Container>
|
||||||
<Component class="org.sleuthkit.autopsy.communications.FiltersPanel" name="filtersPane">
|
<Component class="org.sleuthkit.autopsy.communications.FiltersPanel" name="filtersPane">
|
||||||
|
@ -71,7 +71,7 @@ public final class CVTTopComponent extends TopComponent {
|
|||||||
* via an Eventbus
|
* via an Eventbus
|
||||||
*/
|
*/
|
||||||
CVTEvents.getCVTEventBus().register(this);
|
CVTEvents.getCVTEventBus().register(this);
|
||||||
// CVTEvents.getCVTEventBus().register(vizPanel);
|
CVTEvents.getCVTEventBus().register(vizPanel);
|
||||||
CVTEvents.getCVTEventBus().register(accountsBrowser);
|
CVTEvents.getCVTEventBus().register(accountsBrowser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,13 +88,14 @@ public final class CVTTopComponent extends TopComponent {
|
|||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
|
|
||||||
vizPanel = new VisualizationPanel();
|
|
||||||
browseVisualizeTabPane = new JTabbedPane();
|
browseVisualizeTabPane = new JTabbedPane();
|
||||||
accountsBrowser = new AccountsBrowser();
|
accountsBrowser = new AccountsBrowser();
|
||||||
|
vizPanel = new VisualizationPanel();
|
||||||
filtersPane = new FiltersPanel();
|
filtersPane = new FiltersPanel();
|
||||||
|
|
||||||
browseVisualizeTabPane.setFont(new Font("Tahoma", 0, 18)); // NOI18N
|
browseVisualizeTabPane.setFont(new Font("Tahoma", 0, 18)); // NOI18N
|
||||||
browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N
|
browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N
|
||||||
|
browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N
|
||||||
|
|
||||||
filtersPane.setMinimumSize(new Dimension(256, 495));
|
filtersPane.setMinimumSize(new Dimension(256, 495));
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ public final class CVTTopComponent extends TopComponent {
|
|||||||
.addGap(6, 6, 6)
|
.addGap(6, 6, 6)
|
||||||
.addComponent(filtersPane, GroupLayout.PREFERRED_SIZE, 265, GroupLayout.PREFERRED_SIZE)
|
.addComponent(filtersPane, GroupLayout.PREFERRED_SIZE, 265, GroupLayout.PREFERRED_SIZE)
|
||||||
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(browseVisualizeTabPane, GroupLayout.DEFAULT_SIZE, 786, Short.MAX_VALUE)
|
.addComponent(browseVisualizeTabPane, GroupLayout.PREFERRED_SIZE, 786, Short.MAX_VALUE)
|
||||||
.addContainerGap())
|
.addContainerGap())
|
||||||
);
|
);
|
||||||
layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||||
|
@ -25,7 +25,6 @@ import com.google.common.collect.MultimapBuilder;
|
|||||||
import com.mxgraph.model.mxCell;
|
import com.mxgraph.model.mxCell;
|
||||||
import com.mxgraph.model.mxICell;
|
import com.mxgraph.model.mxICell;
|
||||||
import com.mxgraph.util.mxConstants;
|
import com.mxgraph.util.mxConstants;
|
||||||
import com.mxgraph.view.mxCellState;
|
|
||||||
import com.mxgraph.view.mxGraph;
|
import com.mxgraph.view.mxGraph;
|
||||||
import com.mxgraph.view.mxStylesheet;
|
import com.mxgraph.view.mxStylesheet;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -84,17 +83,19 @@ final class CommunicationsGraph extends mxGraph {
|
|||||||
mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE);
|
mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Map from type specific account identifier to mxCell(vertex). */
|
/** Map from type specific account identifier to mxCell(vertex). */
|
||||||
private final Map<String, mxCell> nodeMap = new HashMap<>();
|
private final Map<String, mxCell> nodeMap = new HashMap<>();
|
||||||
|
|
||||||
/* Map from relationship source (Content) to mxCell (edge). */
|
/** Map from relationship source (Content) to mxCell (edge). */
|
||||||
private final Multimap<Content, mxCell> edgeMap = MultimapBuilder.hashKeys().hashSetValues().build();
|
private final Multimap<Content, mxCell> edgeMap = MultimapBuilder.hashKeys().hashSetValues().build();
|
||||||
private final LockedVertexModel lockedVertexModel;
|
private final LockedVertexModel lockedVertexModel;
|
||||||
|
|
||||||
private final PinnedAccountModel pinnedAccountModel;
|
private final PinnedAccountModel pinnedAccountModel;
|
||||||
|
|
||||||
CommunicationsGraph() {
|
CommunicationsGraph(PinnedAccountModel pinnedAccountModel, LockedVertexModel lockedVertexModel) {
|
||||||
super(mxStylesheet);
|
super(mxStylesheet);
|
||||||
|
this.pinnedAccountModel = pinnedAccountModel;
|
||||||
|
this.lockedVertexModel = lockedVertexModel;
|
||||||
//set fixed properties of graph.
|
//set fixed properties of graph.
|
||||||
setAutoSizeCells(true);
|
setAutoSizeCells(true);
|
||||||
setCellsCloneable(false);
|
setCellsCloneable(false);
|
||||||
@ -113,21 +114,6 @@ final class CommunicationsGraph extends mxGraph {
|
|||||||
setKeepEdgesInBackground(true);
|
setKeepEdgesInBackground(true);
|
||||||
setResetEdgesOnMove(true);
|
setResetEdgesOnMove(true);
|
||||||
setHtmlLabels(true);
|
setHtmlLabels(true);
|
||||||
|
|
||||||
lockedVertexModel = new LockedVertexModel();
|
|
||||||
lockedVertexModel.registerhandler((LockedVertexModel.VertexLockEvent event) -> {
|
|
||||||
if (event.isVertexLocked()) {
|
|
||||||
getView().clear(event.getVertex(), true, true);
|
|
||||||
getView().validate();
|
|
||||||
} else {
|
|
||||||
final mxCellState state = getView().getState(event.getVertex(), true);
|
|
||||||
getView().updateLabel(state);
|
|
||||||
getView().updateLabelBounds(state);
|
|
||||||
getView().updateBoundingBox(state);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pinnedAccountModel = new PinnedAccountModel(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -255,20 +241,20 @@ final class CommunicationsGraph extends mxGraph {
|
|||||||
*/
|
*/
|
||||||
private class RebuildWorker extends SwingWorker<Void, Void> {
|
private class RebuildWorker extends SwingWorker<Void, Void> {
|
||||||
|
|
||||||
private final ProgressIndicator progress;
|
private final ProgressIndicator progressIndicator;
|
||||||
private final CommunicationsManager commsManager;
|
private final CommunicationsManager commsManager;
|
||||||
private final CommunicationsFilter currentFilter;
|
private final CommunicationsFilter currentFilter;
|
||||||
|
|
||||||
RebuildWorker(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) {
|
RebuildWorker(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) {
|
||||||
this.progress = progress;
|
this.progressIndicator = progress;
|
||||||
this.currentFilter = currentFilter;
|
this.currentFilter = currentFilter;
|
||||||
this.commsManager = commsManager;
|
this.commsManager = commsManager;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground() throws Exception {
|
protected Void doInBackground() {
|
||||||
progress.start("Loading accounts");
|
progressIndicator.start("Loading accounts");
|
||||||
int progressCounter = 0;
|
int progressCounter = 0;
|
||||||
try {
|
try {
|
||||||
/**
|
/**
|
||||||
@ -279,18 +265,18 @@ final class CommunicationsGraph extends mxGraph {
|
|||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
final List<AccountDeviceInstance> relatedAccountDeviceInstances =
|
//get accounts related to pinned account
|
||||||
commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter);
|
final List<AccountDeviceInstance> relatedAccountDeviceInstances
|
||||||
|
= commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter);
|
||||||
relatedAccounts.put(adiKey.getAccountDeviceInstance(), adiKey);
|
relatedAccounts.put(adiKey.getAccountDeviceInstance(), adiKey);
|
||||||
getOrCreateVertex(adiKey);
|
getOrCreateVertex(adiKey);
|
||||||
|
|
||||||
//get accounts related to pinned account
|
|
||||||
for (final AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) {
|
for (final AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) {
|
||||||
final long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter);
|
final long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter);
|
||||||
final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount);
|
final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount);
|
||||||
relatedAccounts.put(relatedADI, relatedADIKey); //store related accounts
|
relatedAccounts.put(relatedADI, relatedADIKey); //store related accounts
|
||||||
}
|
}
|
||||||
progress.progress(++progressCounter);
|
progressIndicator.progress(++progressCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<AccountDeviceInstance> accounts = relatedAccounts.keySet();
|
Set<AccountDeviceInstance> accounts = relatedAccounts.keySet();
|
||||||
@ -298,19 +284,24 @@ final class CommunicationsGraph extends mxGraph {
|
|||||||
Map<AccountPair, Long> relationshipCounts = commsManager.getRelationshipCountsPairwise(accounts, currentFilter);
|
Map<AccountPair, Long> relationshipCounts = commsManager.getRelationshipCountsPairwise(accounts, currentFilter);
|
||||||
|
|
||||||
int total = relationshipCounts.size();
|
int total = relationshipCounts.size();
|
||||||
int k = 0;
|
int progress = 0;
|
||||||
progress.switchToDeterminate("", 0, total);
|
String progressText = "";
|
||||||
|
progressIndicator.switchToDeterminate("", 0, total);
|
||||||
for (Map.Entry<AccountPair, Long> entry : relationshipCounts.entrySet()) {
|
for (Map.Entry<AccountPair, Long> entry : relationshipCounts.entrySet()) {
|
||||||
Long count = entry.getValue();
|
Long count = entry.getValue();
|
||||||
AccountPair relationshipKey = entry.getKey();
|
AccountPair relationshipKey = entry.getKey();
|
||||||
AccountDeviceInstanceKey account1 = relatedAccounts.get(relationshipKey.getFirst());
|
AccountDeviceInstanceKey account1 = relatedAccounts.get(relationshipKey.getFirst());
|
||||||
AccountDeviceInstanceKey account2 = relatedAccounts.get(relationshipKey.getSecond());
|
AccountDeviceInstanceKey account2 = relatedAccounts.get(relationshipKey.getSecond());
|
||||||
mxCell addEdge = addOrUpdateEdge(count, account1, account2);
|
|
||||||
progress.progress(addEdge.getId(), k++);
|
if (pinnedAccountModel.isAccountPinned(account1)
|
||||||
|
|| pinnedAccountModel.isAccountPinned(account2)) {
|
||||||
|
mxCell addEdge = addOrUpdateEdge(count, account1, account2);
|
||||||
|
progressText = addEdge.getId();
|
||||||
|
}
|
||||||
|
progressIndicator.progress(progressText, progress++);
|
||||||
}
|
}
|
||||||
} catch (TskCoreException tskCoreException) {
|
} catch (TskCoreException tskCoreException) {
|
||||||
logger.log(Level.SEVERE, "Error", tskCoreException);
|
logger.log(Level.SEVERE, "Error", tskCoreException);
|
||||||
} finally {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -326,7 +317,7 @@ final class CommunicationsGraph extends mxGraph {
|
|||||||
} catch (CancellationException ex) {
|
} catch (CancellationException ex) {
|
||||||
logger.log(Level.INFO, "Graph visualization cancelled");
|
logger.log(Level.INFO, "Graph visualization cancelled");
|
||||||
} finally {
|
} finally {
|
||||||
progress.finish();
|
progressIndicator.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.communications;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public interface EventHandler<T> {
|
|
||||||
|
|
||||||
void handle(T event);
|
|
||||||
}
|
|
@ -18,20 +18,19 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.communications;
|
package org.sleuthkit.autopsy.communications;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
import com.mxgraph.model.mxCell;
|
import com.mxgraph.model.mxCell;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
class LockedVertexModel {
|
/**
|
||||||
|
* Model of which vertices in a graph are locked ( not moveable by layout
|
||||||
void registerhandler(EventHandler<VertexLockEvent> handler) {
|
* algorithms).
|
||||||
eventBus.register(handler);
|
*
|
||||||
}
|
*/
|
||||||
|
final class LockedVertexModel {
|
||||||
void unregisterhandler(EventHandler<VertexLockEvent> handler) {
|
|
||||||
eventBus.unregister(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final EventBus eventBus = new EventBus();
|
private final EventBus eventBus = new EventBus();
|
||||||
|
|
||||||
@ -42,30 +41,34 @@ class LockedVertexModel {
|
|||||||
*/
|
*/
|
||||||
private final Set<mxCell> lockedVertices = new HashSet<>();
|
private final Set<mxCell> lockedVertices = new HashSet<>();
|
||||||
|
|
||||||
LockedVertexModel() {
|
void registerhandler(Object handler) {
|
||||||
|
eventBus.register(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterhandler(Object handler) {
|
||||||
|
eventBus.unregister(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lock the given vertex so that applying a layout algorithm doesn't move
|
* Lock the given vertices so that applying a layout algorithm doesn't move
|
||||||
* it. The user can still manually position the vertex.
|
* them. The user can still manually position the vertices.
|
||||||
*
|
*
|
||||||
* @param vertex The vertex to lock.
|
* @param vertex The vertex to lock.
|
||||||
*/
|
*/
|
||||||
void lockVertex(mxCell vertex) {
|
void lock(Collection<mxCell> vertices) {
|
||||||
lockedVertices.add(vertex);
|
lockedVertices.addAll(vertices);
|
||||||
eventBus.post(new VertexLockEvent(vertex, true));
|
eventBus.post(new VertexLockEvent(true, vertices));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lock the given vertex so that applying a layout algorithm can move it.
|
* Unlock the given vertices so that applying a layout algorithm can move
|
||||||
|
* them.
|
||||||
*
|
*
|
||||||
* @param vertex The vertex to unlock.
|
* @param vertex The vertex to unlock.
|
||||||
*/
|
*/
|
||||||
void unlockVertex(mxCell vertex) {
|
void unlock(Collection<mxCell> vertices) {
|
||||||
lockedVertices.remove(vertex);
|
lockedVertices.removeAll(vertices);
|
||||||
eventBus.post(new VertexLockEvent(vertex, false));
|
eventBus.post(new VertexLockEvent(false, vertices));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isVertexLocked(mxCell vertex) {
|
boolean isVertexLocked(mxCell vertex) {
|
||||||
@ -77,21 +80,36 @@ class LockedVertexModel {
|
|||||||
lockedVertices.clear();
|
lockedVertices.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static class VertexLockEvent {
|
boolean isEmpty() {
|
||||||
|
return lockedVertices.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
private final mxCell vertex;
|
/**
|
||||||
|
* Event that represents a change in the locked state of one or more
|
||||||
|
* vertices.
|
||||||
|
*/
|
||||||
|
final static class VertexLockEvent {
|
||||||
|
|
||||||
public mxCell getVertex() {
|
private final boolean locked;
|
||||||
return vertex;
|
private final Set<mxCell> vertices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The vertices whose locked state has changed.
|
||||||
|
*/
|
||||||
|
public Set<mxCell> getVertices() {
|
||||||
|
return vertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVertexLocked() {
|
/**
|
||||||
|
* @return True if the vertices are locked, False if the vertices are
|
||||||
|
* unlocked.
|
||||||
|
*/
|
||||||
|
public boolean isLocked() {
|
||||||
return locked;
|
return locked;
|
||||||
}
|
}
|
||||||
private final boolean locked;
|
|
||||||
|
|
||||||
VertexLockEvent(mxCell vertex, boolean locked) {
|
VertexLockEvent(boolean locked, Collection< mxCell> vertices) {
|
||||||
this.vertex = vertex;
|
this.vertices = ImmutableSet.copyOf(vertices);
|
||||||
this.locked = locked;
|
this.locked = locked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,6 @@ import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
|
|||||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
||||||
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
||||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The right hand side of the CVT. Has a DataResultPanel to show a listing of
|
* The right hand side of the CVT. Has a DataResultPanel to show a listing of
|
||||||
@ -151,10 +150,10 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov
|
|||||||
//Use lookup here?
|
//Use lookup here?
|
||||||
final AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0];
|
final AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0];
|
||||||
|
|
||||||
final Set<AccountDeviceInstance> accountDeviceInstances = new HashSet<>();
|
final Set<AccountDeviceInstanceKey> accountDeviceInstances = new HashSet<>();
|
||||||
for (final Node n : selectedNodes) {
|
for (final Node n : selectedNodes) {
|
||||||
//Use lookup here?
|
//Use lookup here?
|
||||||
accountDeviceInstances.add(((AccountDeviceInstanceNode) n).getAccountDeviceInstance());
|
accountDeviceInstances.add(((AccountDeviceInstanceNode) n).getAccountDeviceInstanceKey());
|
||||||
}
|
}
|
||||||
return SelectionNode.createFromAccounts(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager());
|
return SelectionNode.createFromAccounts(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager());
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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.communications;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import org.openide.util.ImageUtilities;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action that pins the AccountDevicesIntanceKeys in the ActionsGlobalContext to
|
||||||
|
* the visualizaion
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({"PinAccountsAction.pluralText=Add Selected Accounts to Visualization",
|
||||||
|
"PinAccountsAction.singularText=Add Selected Account to Visualization"})
|
||||||
|
final class PinAccountsAction extends AbstractCVTAction {
|
||||||
|
|
||||||
|
static private final ImageIcon ICON = ImageUtilities.loadImageIcon(
|
||||||
|
"/org/sleuthkit/autopsy/communications/images/marker--plus.png", false);
|
||||||
|
private static final String SINGULAR_TEXT = Bundle.PinAccountsAction_singularText();
|
||||||
|
private static final String PLURAL_TEXT = Bundle.PinAccountsAction_pluralText();
|
||||||
|
|
||||||
|
private static final PinAccountsAction instance = new PinAccountsAction();
|
||||||
|
|
||||||
|
static PinAccountsAction getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent event) {
|
||||||
|
CVTEvents.getCVTEventBus().post(new CVTEvents.PinAccountsEvent(getSelectedAccounts(), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getActionDisplayName() {
|
||||||
|
return getSelectedAccounts().size() > 1 ? PLURAL_TEXT : SINGULAR_TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ImageIcon getIcon() {
|
||||||
|
return ICON;
|
||||||
|
}
|
||||||
|
}
|
@ -19,22 +19,31 @@
|
|||||||
package org.sleuthkit.autopsy.communications;
|
package org.sleuthkit.autopsy.communications;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.eventbus.EventBus;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model of what accounts are pinned to a visualization.
|
||||||
|
*/
|
||||||
class PinnedAccountModel {
|
class PinnedAccountModel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set of AccountDeviceInstanceKeys that are 'Pinned' to this graph. Pinned
|
* Set of AccountDeviceInstanceKeys that are 'Pinned' to the graph. Pinned
|
||||||
* accounts are shown regardless of filters, and accounts that are related
|
* accounts are shown regardless of filters, and accounts that are related
|
||||||
* to pinned accounts and pass the filters are show. Pinning accounts is the
|
* to pinned accounts and pass the filters are show. Pinning accounts is the
|
||||||
* primary way to populate the graph.
|
* primary way to populate the graph.
|
||||||
*/
|
*/
|
||||||
private final Set<AccountDeviceInstanceKey> pinnedAccountDevices = new HashSet<>();
|
private final Set<AccountDeviceInstanceKey> pinnedAccountDevices = new HashSet<>();
|
||||||
private final CommunicationsGraph graph;
|
|
||||||
|
|
||||||
PinnedAccountModel(CommunicationsGraph graph) {
|
private final EventBus eventBus = new EventBus();
|
||||||
this.graph = graph;
|
|
||||||
|
void registerhandler(Object handler) {
|
||||||
|
eventBus.register(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterhandler(Object handler) {
|
||||||
|
eventBus.unregister(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isAccountPinned(AccountDeviceInstanceKey account) {
|
boolean isAccountPinned(AccountDeviceInstanceKey account) {
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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.communications;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import org.openide.util.ImageUtilities;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action that clears any pinned accounts and pins the AcountDevicesInstanceKeys
|
||||||
|
* in the ActionsGlobalContext to the visualization.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages(value = {"ResetAndPinAccountsAction.singularText=Visualize Only Selected Account",
|
||||||
|
"ResetAndPinAccountsAction.pluralText=Visualize Only Selected Accounts"})
|
||||||
|
final class ResetAndPinAccountsAction extends AbstractCVTAction {
|
||||||
|
|
||||||
|
private static final ImageIcon ICON = ImageUtilities.loadImageIcon(
|
||||||
|
"/org/sleuthkit/autopsy/communications/images/marker--pin.png", false);
|
||||||
|
private static final String SINGULAR_TEXT = Bundle.ResetAndPinAccountsAction_singularText();
|
||||||
|
private static final String PLURAL_TEXT = Bundle.ResetAndPinAccountsAction_pluralText();
|
||||||
|
|
||||||
|
private static final ResetAndPinAccountsAction instance = new ResetAndPinAccountsAction();
|
||||||
|
|
||||||
|
static ResetAndPinAccountsAction getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent event) {
|
||||||
|
CVTEvents.getCVTEventBus().post(new CVTEvents.PinAccountsEvent(getSelectedAccounts(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getActionDisplayName() {
|
||||||
|
return getSelectedAccounts().size() > 1 ? PLURAL_TEXT : SINGULAR_TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ImageIcon getIcon() {
|
||||||
|
return ICON;
|
||||||
|
}
|
||||||
|
}
|
@ -24,10 +24,13 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.openide.nodes.AbstractNode;
|
import org.openide.nodes.AbstractNode;
|
||||||
import org.openide.nodes.ChildFactory;
|
import org.openide.nodes.ChildFactory;
|
||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
|
import org.openide.util.Lookup;
|
||||||
|
import org.openide.util.lookup.Lookups;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
@ -44,23 +47,27 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
*/
|
*/
|
||||||
final class SelectionNode extends AbstractNode {
|
final class SelectionNode extends AbstractNode {
|
||||||
|
|
||||||
private SelectionNode(Children children) {
|
private SelectionNode(Children children, Lookup lookup) {
|
||||||
super(children);
|
super(children, lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SelectionNode createFromAccountsAndRelationships(
|
static SelectionNode createFromAccountsAndRelationships(
|
||||||
Set<Content> edgeRelationshipArtifacts,
|
Set<Content> edgeRelationshipArtifacts,
|
||||||
Set<AccountDeviceInstance> accountDeviceInstances,
|
Set<AccountDeviceInstanceKey> accountDeviceInstanceKeys,
|
||||||
CommunicationsFilter filter,
|
CommunicationsFilter filter,
|
||||||
CommunicationsManager commsManager) {
|
CommunicationsManager commsManager) {
|
||||||
|
|
||||||
|
Set<AccountDeviceInstance> accountDeviceInstances = accountDeviceInstanceKeys.stream()
|
||||||
|
.map(AccountDeviceInstanceKey::getAccountDeviceInstance)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
SelectionNode node = new SelectionNode(Children.create(
|
SelectionNode node = new SelectionNode(Children.create(
|
||||||
new RelationshipChildren(
|
new RelationshipChildren(
|
||||||
edgeRelationshipArtifacts,
|
edgeRelationshipArtifacts,
|
||||||
accountDeviceInstances,
|
accountDeviceInstances,
|
||||||
commsManager,
|
commsManager,
|
||||||
filter),
|
filter),
|
||||||
true));
|
true), Lookups.fixed(accountDeviceInstanceKeys.toArray()));
|
||||||
|
|
||||||
//This is not good for internationalization!!!
|
//This is not good for internationalization!!!
|
||||||
String name = "";
|
String name = "";
|
||||||
@ -82,7 +89,7 @@ final class SelectionNode extends AbstractNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static SelectionNode createFromAccounts(
|
static SelectionNode createFromAccounts(
|
||||||
Set<AccountDeviceInstance> accountDeviceInstances,
|
Set<AccountDeviceInstanceKey> accountDeviceInstances,
|
||||||
CommunicationsFilter filter,
|
CommunicationsFilter filter,
|
||||||
CommunicationsManager commsManager) {
|
CommunicationsManager commsManager) {
|
||||||
|
|
||||||
@ -122,9 +129,9 @@ final class SelectionNode extends AbstractNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node createNodeForKey(Content t) {
|
protected Node createNodeForKey(Content content) {
|
||||||
if (t instanceof BlackboardArtifact) {
|
if (content instanceof BlackboardArtifact) {
|
||||||
return new RelationshipNode((BlackboardArtifact) t);
|
return new RelationshipNode((BlackboardArtifact) content);
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content.");
|
throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content.");
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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.communications;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import org.openide.util.ImageUtilities;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action that unpins the AcccountDeviceInstanceKeys in the ActionsGlobalContext
|
||||||
|
* form the visualization.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({"UnpinAccountsAction.pluralText=Remove Selected Accounts",
|
||||||
|
"UnpinAccountsAction.singularText=Remove Selected Account"})
|
||||||
|
final class UnpinAccountsAction extends AbstractCVTAction {
|
||||||
|
|
||||||
|
static final private ImageIcon ICON = ImageUtilities.loadImageIcon(
|
||||||
|
"/org/sleuthkit/autopsy/communications/images/marker--minus.png", false);
|
||||||
|
private static final String SINGULAR_TEXT = Bundle.UnpinAccountsAction_singularText();
|
||||||
|
private static final String PLURAL_TEXT = Bundle.UnpinAccountsAction_pluralText();
|
||||||
|
|
||||||
|
private static final UnpinAccountsAction instance = new UnpinAccountsAction();
|
||||||
|
|
||||||
|
static UnpinAccountsAction getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(final ActionEvent event) {
|
||||||
|
CVTEvents.getCVTEventBus().post(new CVTEvents.UnpinAccountsEvent(getSelectedAccounts()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getActionDisplayName() {
|
||||||
|
return getSelectedAccounts().size() > 1 ? PLURAL_TEXT : SINGULAR_TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ImageIcon getIcon() {
|
||||||
|
return ICON;
|
||||||
|
}
|
||||||
|
}
|
@ -68,15 +68,15 @@
|
|||||||
<SubComponents>
|
<SubComponents>
|
||||||
<Component class="javax.swing.JTextArea" name="jTextArea1">
|
<Component class="javax.swing.JTextArea" name="jTextArea1">
|
||||||
<Properties>
|
<Properties>
|
||||||
|
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||||
|
<Color blue="f0" green="f0" red="f0" type="rgb"/>
|
||||||
|
</Property>
|
||||||
<Property name="columns" type="int" value="20"/>
|
<Property name="columns" type="int" value="20"/>
|
||||||
<Property name="lineWrap" type="boolean" value="true"/>
|
<Property name="lineWrap" type="boolean" value="true"/>
|
||||||
<Property name="rows" type="int" value="5"/>
|
<Property name="rows" type="int" value="5"/>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.jTextArea1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.jTextArea1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
|
||||||
<Color blue="f0" green="f0" red="f0" type="rgb"/>
|
|
||||||
</Property>
|
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
@ -166,9 +166,6 @@
|
|||||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="hierarchyLayoutButtonActionPerformed"/>
|
|
||||||
</Events>
|
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="fastOrganicLayoutButton">
|
<Component class="javax.swing.JButton" name="fastOrganicLayoutButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
@ -179,9 +176,6 @@
|
|||||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="fastOrganicLayoutButtonActionPerformed"/>
|
|
||||||
</Events>
|
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="organicLayoutButton">
|
<Component class="javax.swing.JButton" name="organicLayoutButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
@ -192,9 +186,6 @@
|
|||||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="organicLayoutButtonActionPerformed"/>
|
|
||||||
</Events>
|
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="circleLayoutButton">
|
<Component class="javax.swing.JButton" name="circleLayoutButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
@ -205,9 +196,6 @@
|
|||||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="circleLayoutButtonActionPerformed"/>
|
|
||||||
</Events>
|
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator1">
|
<Component class="javax.swing.JToolBar$Separator" name="jSeparator1">
|
||||||
<Properties>
|
<Properties>
|
||||||
@ -222,11 +210,11 @@
|
|||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomOutButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomOutButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="focusable" type="boolean" value="false"/>
|
|
||||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
|
||||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomOutButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomOutButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
@ -241,11 +229,11 @@
|
|||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomInButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomInButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="focusable" type="boolean" value="false"/>
|
|
||||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
|
||||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomInButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomInButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
@ -260,11 +248,11 @@
|
|||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomActualButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomActualButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="focusable" type="boolean" value="false"/>
|
|
||||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
|
||||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomActualButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.zoomActualButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
@ -279,11 +267,11 @@
|
|||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.fitZoomButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.fitZoomButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="focusable" type="boolean" value="false"/>
|
|
||||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
|
||||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.fitZoomButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.fitZoomButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
|
@ -28,7 +28,6 @@ import com.mxgraph.model.mxCell;
|
|||||||
import com.mxgraph.model.mxICell;
|
import com.mxgraph.model.mxICell;
|
||||||
import com.mxgraph.swing.handler.mxRubberband;
|
import com.mxgraph.swing.handler.mxRubberband;
|
||||||
import com.mxgraph.swing.mxGraphComponent;
|
import com.mxgraph.swing.mxGraphComponent;
|
||||||
import com.mxgraph.swing.util.mxMorphing;
|
|
||||||
import com.mxgraph.util.mxEvent;
|
import com.mxgraph.util.mxEvent;
|
||||||
import com.mxgraph.util.mxEventObject;
|
import com.mxgraph.util.mxEventObject;
|
||||||
import com.mxgraph.util.mxEventSource;
|
import com.mxgraph.util.mxEventSource;
|
||||||
@ -36,11 +35,14 @@ import com.mxgraph.util.mxPoint;
|
|||||||
import com.mxgraph.util.mxRectangle;
|
import com.mxgraph.util.mxRectangle;
|
||||||
import com.mxgraph.util.mxUndoManager;
|
import com.mxgraph.util.mxUndoManager;
|
||||||
import com.mxgraph.util.mxUndoableEdit;
|
import com.mxgraph.util.mxUndoableEdit;
|
||||||
|
import com.mxgraph.view.mxCellState;
|
||||||
import com.mxgraph.view.mxGraph;
|
import com.mxgraph.view.mxGraph;
|
||||||
|
import com.mxgraph.view.mxGraphView;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
import java.awt.Frame;
|
import java.awt.Frame;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
@ -51,18 +53,23 @@ import java.beans.PropertyChangeEvent;
|
|||||||
import java.beans.PropertyVetoException;
|
import java.beans.PropertyVetoException;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import static java.util.Collections.singleton;
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JPopupMenu;
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.JSplitPane;
|
import javax.swing.JSplitPane;
|
||||||
@ -80,12 +87,11 @@ import org.openide.util.Lookup;
|
|||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.lookup.ProxyLookup;
|
import org.openide.util.lookup.ProxyLookup;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||||
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
|
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
|
||||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
|
||||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
@ -101,23 +107,17 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
* CVTTopComponent when this tab is active allowing for context sensitive
|
* CVTTopComponent when this tab is active allowing for context sensitive
|
||||||
* actions to work correctly.
|
* actions to work correctly.
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel")
|
|
||||||
final public class VisualizationPanel extends JPanel implements Lookup.Provider {
|
final public class VisualizationPanel extends JPanel implements Lookup.Provider {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName());
|
private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName());
|
||||||
private static final String BASE_IMAGE_PATH = "/org/sleuthkit/autopsy/communications/images";
|
private static final String BASE_IMAGE_PATH = "/org/sleuthkit/autopsy/communications/images";
|
||||||
static final private ImageIcon pinIcon
|
|
||||||
= new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/marker--pin.png"));
|
|
||||||
static final private ImageIcon addPinIcon
|
|
||||||
= new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/marker--plus.png"));
|
|
||||||
static final private ImageIcon unpinIcon
|
|
||||||
= new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/marker--minus.png"));
|
|
||||||
static final private ImageIcon unlockIcon
|
static final private ImageIcon unlockIcon
|
||||||
= new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_unlocked.png"));
|
= new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_unlocked.png"));
|
||||||
static final private ImageIcon lockIcon
|
static final private ImageIcon lockIcon
|
||||||
= new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_locked.png"));
|
= new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_locked.png"));
|
||||||
|
|
||||||
|
@NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel")
|
||||||
private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
|
private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
|
||||||
|
|
||||||
private final ExplorerManager vizEM = new ExplorerManager();
|
private final ExplorerManager vizEM = new ExplorerManager();
|
||||||
@ -132,27 +132,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
private final CommunicationsGraph graph;
|
private final CommunicationsGraph graph;
|
||||||
|
|
||||||
private final mxUndoManager undoManager = new mxUndoManager();
|
private final mxUndoManager undoManager = new mxUndoManager();
|
||||||
private final mxRubberband rubberband;
|
private final mxRubberband rubberband; //NOPMD We keep a referenec as insurance to prevent garbage collection
|
||||||
private final mxFastOrganicLayout fastOrganicLayout;
|
|
||||||
private final mxCircleLayout circleLayout;
|
|
||||||
private final mxOrganicLayout organicLayout;
|
|
||||||
private final mxHierarchicalLayout hierarchicalLayout;
|
|
||||||
|
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
private SwingWorker<?, ?> worker;
|
private SwingWorker<?, ?> worker;
|
||||||
private final PinnedAccountModel pinnedAccountModel;
|
private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
|
||||||
private final LockedVertexModel lockedVertexModel;
|
private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
|
||||||
|
|
||||||
|
private final Map<NamedGraphLayout, JButton> layoutButtons = new HashMap<>();
|
||||||
|
private NamedGraphLayout currentLayout;
|
||||||
|
|
||||||
public VisualizationPanel() {
|
public VisualizationPanel() {
|
||||||
initComponents();
|
initComponents();
|
||||||
graph = new CommunicationsGraph();
|
|
||||||
pinnedAccountModel = graph.getPinnedAccountModel();
|
|
||||||
lockedVertexModel = graph.getLockedVertexModel();
|
|
||||||
|
|
||||||
fastOrganicLayout = new mxFastOrganicLayoutImpl(graph);
|
graph = new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
|
||||||
circleLayout = new mxCircleLayoutImpl(graph);
|
|
||||||
organicLayout = new mxOrganicLayoutImpl(graph);
|
|
||||||
hierarchicalLayout = new mxHierarchicalLayoutImpl(graph);
|
|
||||||
|
|
||||||
graphComponent = new mxGraphComponent(graph);
|
graphComponent = new mxGraphComponent(graph);
|
||||||
graphComponent.setAutoExtend(true);
|
graphComponent.setAutoExtend(true);
|
||||||
@ -166,123 +159,90 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
graphComponent.setBackground(Color.WHITE);
|
graphComponent.setBackground(Color.WHITE);
|
||||||
borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
|
borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
|
||||||
|
|
||||||
//install rubber band selection handler
|
//install rubber band other handlers
|
||||||
rubberband = new mxRubberband(graphComponent);
|
rubberband = new mxRubberband(graphComponent);
|
||||||
|
|
||||||
|
lockedVertexModel.registerhandler(this);
|
||||||
|
|
||||||
final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
|
final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
|
||||||
-> zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
|
-> zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
|
||||||
graph.getView().addListener(mxEvent.SCALE, scaleListener);
|
graph.getView().addListener(mxEvent.SCALE, scaleListener);
|
||||||
graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
|
graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
|
||||||
|
|
||||||
graphComponent.getGraphControl().addMouseWheelListener(new MouseAdapter() {
|
final GraphMouseListener graphMouseListener = new GraphMouseListener();
|
||||||
/**
|
graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
|
||||||
* Translate mouse wheel events into zooming.
|
graphComponent.getGraphControl().addMouseListener(graphMouseListener);
|
||||||
*
|
|
||||||
* @param event The MouseWheelEvent
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void mouseWheelMoved(final MouseWheelEvent event) {
|
|
||||||
super.mouseWheelMoved(event);
|
|
||||||
if (event.getPreciseWheelRotation() > 0) {
|
|
||||||
graphComponent.zoomIn();
|
|
||||||
} else if (event.getPreciseWheelRotation() < 0) {
|
|
||||||
graphComponent.zoomOut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
graphComponent.getGraphControl().addMouseListener(new MouseAdapter() {
|
|
||||||
/**
|
|
||||||
* Right click handler: show context menu.
|
|
||||||
*
|
|
||||||
* @param event The MouseEvent
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(final MouseEvent event) {
|
|
||||||
super.mouseClicked(event);
|
|
||||||
if (SwingUtilities.isRightMouseButton(event)) {
|
|
||||||
final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(), event.getY());
|
|
||||||
if (cellAt != null && cellAt.isVertex()) {
|
|
||||||
final JPopupMenu jPopupMenu = new JPopupMenu();
|
|
||||||
final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
|
|
||||||
|
|
||||||
if (lockedVertexModel.isVertexLocked(cellAt)) {
|
|
||||||
jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock " + cellAt.getId(), unlockIcon) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent event) {
|
|
||||||
lockedVertexModel.unlockVertex(cellAt);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
jPopupMenu.add(new JMenuItem(new AbstractAction("Lock " + cellAt.getId(), lockIcon) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent event) {
|
|
||||||
lockedVertexModel.lockVertex(cellAt);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
if (pinnedAccountModel.isAccountPinned(adiKey)) {
|
|
||||||
jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin " + cellAt.getId(), unpinIcon) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent event) {
|
|
||||||
handleUnPinEvent(new CVTEvents.UnpinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue())));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
jPopupMenu.add(new JMenuItem(new AbstractAction("Pin " + cellAt.getId(), addPinIcon) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent event) {
|
|
||||||
handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
jPopupMenu.add(new JMenuItem(new AbstractAction("Pin only " + cellAt.getId(), pinIcon) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent event) {
|
|
||||||
handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), true));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
jPopupMenu.show(graphComponent.getGraphControl(), event.getX(), event.getY());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
final MessageBrowser messageBrowser = new MessageBrowser(vizEM, gacEM);
|
final MessageBrowser messageBrowser = new MessageBrowser(vizEM, gacEM);
|
||||||
|
|
||||||
splitPane.setRightComponent(messageBrowser);
|
splitPane.setRightComponent(messageBrowser);
|
||||||
|
|
||||||
proxyLookup = new ProxyLookup(
|
proxyLookup = new ProxyLookup(
|
||||||
messageBrowser.getLookup(),
|
ExplorerUtils.createLookup(vizEM, getActionMap()),
|
||||||
ExplorerUtils.createLookup(vizEM, getActionMap()));
|
messageBrowser.getLookup()
|
||||||
|
);
|
||||||
|
|
||||||
//feed selection to explorermanager
|
//feed selection to explorermanager
|
||||||
graph.getSelectionModel().addListener(null, new SelectionListener());
|
graph.getSelectionModel().addListener(mxEvent.CHANGE, new SelectionListener());
|
||||||
final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
|
final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
|
||||||
-> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty("edit"));
|
-> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty("edit"));
|
||||||
|
|
||||||
graph.getModel().addListener(mxEvent.UNDO, undoListener);
|
graph.getModel().addListener(mxEvent.UNDO, undoListener);
|
||||||
graph.getView().addListener(mxEvent.UNDO, undoListener);
|
graph.getView().addListener(mxEvent.UNDO, undoListener);
|
||||||
|
|
||||||
|
FastOrganicLayoutImpl fastOrganicLayout = new FastOrganicLayoutImpl(graph);
|
||||||
|
CircleLayoutImpl circleLayout = new CircleLayoutImpl(graph);
|
||||||
|
OrganicLayoutImpl organicLayout = new OrganicLayoutImpl(graph);
|
||||||
|
organicLayout.setMaxIterations(10);
|
||||||
|
HierarchicalLayoutImpl hierarchyLayout = new HierarchicalLayoutImpl(graph);
|
||||||
|
|
||||||
|
//local method to configure layout buttons
|
||||||
|
BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
|
||||||
|
layoutButtons.put(layout, layoutButton);
|
||||||
|
layoutButton.addActionListener(event -> applyLayout(layout));
|
||||||
|
};
|
||||||
|
//configure layout buttons.
|
||||||
|
configure.accept(circleLayoutButton, circleLayout);
|
||||||
|
configure.accept(organicLayoutButton, organicLayout);
|
||||||
|
configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
|
||||||
|
configure.accept(hierarchyLayoutButton, hierarchyLayout);
|
||||||
|
|
||||||
|
applyLayout(fastOrganicLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param layoutButton the value of layoutButton
|
||||||
|
* @param layout the value of layout
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|
||||||
public Lookup getLookup() {
|
public Lookup getLookup() {
|
||||||
return proxyLookup;
|
return proxyLookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
void handleUnPinEvent(final CVTEvents.UnpinAccountsEvent pinEvent) {
|
void handle(LockedVertexModel.VertexLockEvent event) {
|
||||||
|
final Set<mxCell> vertices = event.getVertices();
|
||||||
|
mxGraphView view = graph.getView();
|
||||||
|
vertices.forEach(vertex -> {
|
||||||
|
final mxCellState state = view.getState(vertex, true);
|
||||||
|
view.updateLabel(state);
|
||||||
|
view.updateLabelBounds(state);
|
||||||
|
view.updateBoundingBox(state);
|
||||||
|
graphComponent.redraw(state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
void handle(final CVTEvents.UnpinAccountsEvent pinEvent) {
|
||||||
graph.getModel().beginUpdate();
|
graph.getModel().beginUpdate();
|
||||||
pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
|
pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
|
||||||
graph.clear();
|
graph.clear();
|
||||||
rebuildGraph();
|
rebuildGraph();
|
||||||
// Updates the display
|
// Updates the display
|
||||||
graph.getModel().endUpdate();
|
graph.getModel().endUpdate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
void handlePinEvent(final CVTEvents.PinAccountsEvent pinEvent) {
|
void handle(final CVTEvents.PinAccountsEvent pinEvent) {
|
||||||
graph.getModel().beginUpdate();
|
graph.getModel().beginUpdate();
|
||||||
if (pinEvent.isReplace()) {
|
if (pinEvent.isReplace()) {
|
||||||
graph.resetGraph();
|
graph.resetGraph();
|
||||||
@ -291,19 +251,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
rebuildGraph();
|
rebuildGraph();
|
||||||
// Updates the display
|
// Updates the display
|
||||||
graph.getModel().endUpdate();
|
graph.getModel().endUpdate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
void handleFilterEvent(final CVTEvents.FilterChangeEvent filterChangeEvent) {
|
void handle(final CVTEvents.FilterChangeEvent filterChangeEvent) {
|
||||||
|
|
||||||
graph.getModel().beginUpdate();
|
graph.getModel().beginUpdate();
|
||||||
graph.clear();
|
graph.clear();
|
||||||
currentFilter = filterChangeEvent.getNewFilter();
|
currentFilter = filterChangeEvent.getNewFilter();
|
||||||
rebuildGraph();
|
rebuildGraph();
|
||||||
// Updates the display
|
// Updates the display
|
||||||
graph.getModel().endUpdate();
|
graph.getModel().endUpdate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
@ -328,19 +285,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
if (worker.isCancelled()) {
|
if (worker.isCancelled()) {
|
||||||
graph.resetGraph();
|
graph.resetGraph();
|
||||||
rebuildGraph();
|
rebuildGraph();
|
||||||
} else if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) {
|
|
||||||
applyOrganicLayout(10);
|
|
||||||
} else {
|
|
||||||
JOptionPane.showMessageDialog(this,
|
|
||||||
"Too many accounts, layout aborted.",
|
|
||||||
"Autopsy",
|
|
||||||
JOptionPane.WARNING_MESSAGE);
|
|
||||||
}
|
}
|
||||||
|
applyLayout(currentLayout);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.execute();
|
worker.execute();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,7 +307,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex);
|
logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
|
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> {
|
||||||
graph.getModel().beginUpdate();
|
graph.getModel().beginUpdate();
|
||||||
try {
|
try {
|
||||||
graph.resetGraph();
|
graph.resetGraph();
|
||||||
@ -374,15 +324,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex);
|
logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeNotify() {
|
|
||||||
super.removeNotify();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called from within the constructor to initialize the form.
|
* 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
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
@ -419,11 +363,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
|
|
||||||
borderLayoutPanel.setLayout(new BorderLayout());
|
borderLayoutPanel.setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
jTextArea1.setBackground(new Color(240, 240, 240));
|
||||||
jTextArea1.setColumns(20);
|
jTextArea1.setColumns(20);
|
||||||
jTextArea1.setLineWrap(true);
|
jTextArea1.setLineWrap(true);
|
||||||
jTextArea1.setRows(5);
|
jTextArea1.setRows(5);
|
||||||
jTextArea1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N
|
jTextArea1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N
|
||||||
jTextArea1.setBackground(new Color(240, 240, 240));
|
|
||||||
|
|
||||||
GroupLayout placeHolderPanelLayout = new GroupLayout(placeHolderPanel);
|
GroupLayout placeHolderPanelLayout = new GroupLayout(placeHolderPanel);
|
||||||
placeHolderPanel.setLayout(placeHolderPanelLayout);
|
placeHolderPanel.setLayout(placeHolderPanelLayout);
|
||||||
@ -448,49 +392,29 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
hierarchyLayoutButton.setFocusable(false);
|
hierarchyLayoutButton.setFocusable(false);
|
||||||
hierarchyLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
hierarchyLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
hierarchyLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
hierarchyLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
hierarchyLayoutButton.addActionListener(new ActionListener() {
|
|
||||||
public void actionPerformed(ActionEvent evt) {
|
|
||||||
hierarchyLayoutButtonActionPerformed(evt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N
|
fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N
|
||||||
fastOrganicLayoutButton.setFocusable(false);
|
fastOrganicLayoutButton.setFocusable(false);
|
||||||
fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
fastOrganicLayoutButton.addActionListener(new ActionListener() {
|
|
||||||
public void actionPerformed(ActionEvent evt) {
|
|
||||||
fastOrganicLayoutButtonActionPerformed(evt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
organicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.organicLayoutButton.text")); // NOI18N
|
organicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.organicLayoutButton.text")); // NOI18N
|
||||||
organicLayoutButton.setFocusable(false);
|
organicLayoutButton.setFocusable(false);
|
||||||
organicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
organicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
organicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
organicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
organicLayoutButton.addActionListener(new ActionListener() {
|
|
||||||
public void actionPerformed(ActionEvent evt) {
|
|
||||||
organicLayoutButtonActionPerformed(evt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
circleLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.circleLayoutButton.text")); // NOI18N
|
circleLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.circleLayoutButton.text")); // NOI18N
|
||||||
circleLayoutButton.setFocusable(false);
|
circleLayoutButton.setFocusable(false);
|
||||||
circleLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
circleLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
circleLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
circleLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
circleLayoutButton.addActionListener(new ActionListener() {
|
|
||||||
public void actionPerformed(ActionEvent evt) {
|
|
||||||
circleLayoutButtonActionPerformed(evt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
jSeparator1.setOrientation(SwingConstants.VERTICAL);
|
jSeparator1.setOrientation(SwingConstants.VERTICAL);
|
||||||
|
|
||||||
zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N
|
zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N
|
||||||
zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N
|
zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N
|
||||||
|
zoomOutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N
|
||||||
zoomOutButton.setFocusable(false);
|
zoomOutButton.setFocusable(false);
|
||||||
zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
zoomOutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N
|
|
||||||
zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
zoomOutButton.addActionListener(new ActionListener() {
|
zoomOutButton.addActionListener(new ActionListener() {
|
||||||
public void actionPerformed(ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
@ -500,9 +424,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
|
|
||||||
zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N
|
zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N
|
||||||
zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N
|
zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N
|
||||||
|
zoomInButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.toolTipText")); // NOI18N
|
||||||
zoomInButton.setFocusable(false);
|
zoomInButton.setFocusable(false);
|
||||||
zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
zoomInButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.toolTipText")); // NOI18N
|
|
||||||
zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
zoomInButton.addActionListener(new ActionListener() {
|
zoomInButton.addActionListener(new ActionListener() {
|
||||||
public void actionPerformed(ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
@ -512,9 +436,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
|
|
||||||
zoomActualButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N
|
zoomActualButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N
|
||||||
zoomActualButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N
|
zoomActualButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N
|
||||||
|
zoomActualButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.toolTipText")); // NOI18N
|
||||||
zoomActualButton.setFocusable(false);
|
zoomActualButton.setFocusable(false);
|
||||||
zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
zoomActualButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.toolTipText")); // NOI18N
|
|
||||||
zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
zoomActualButton.addActionListener(new ActionListener() {
|
zoomActualButton.addActionListener(new ActionListener() {
|
||||||
public void actionPerformed(ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
@ -524,9 +448,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
|
|
||||||
fitZoomButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N
|
fitZoomButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N
|
||||||
fitZoomButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N
|
fitZoomButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N
|
||||||
|
fitZoomButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.toolTipText")); // NOI18N
|
||||||
fitZoomButton.setFocusable(false);
|
fitZoomButton.setFocusable(false);
|
||||||
fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||||
fitZoomButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.toolTipText")); // NOI18N
|
|
||||||
fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||||
fitZoomButton.addActionListener(new ActionListener() {
|
fitZoomButton.addActionListener(new ActionListener() {
|
||||||
public void actionPerformed(ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
@ -626,21 +550,55 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
graphComponent.zoomOut();
|
graphComponent.zoomOut();
|
||||||
}//GEN-LAST:event_zoomOutButtonActionPerformed
|
}//GEN-LAST:event_zoomOutButtonActionPerformed
|
||||||
|
|
||||||
private void circleLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_circleLayoutButtonActionPerformed
|
/**
|
||||||
morph(circleLayout);
|
* Apply the given layout. The given layout becomes the current layout. The
|
||||||
}//GEN-LAST:event_circleLayoutButtonActionPerformed
|
* layout is computed in the background.
|
||||||
|
*
|
||||||
|
* @param layout The layout to apply.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({"VisualizationPanel.computingLayout=Computing Layout",
|
||||||
|
"# {0} - layout name",
|
||||||
|
"VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
|
||||||
|
"# {0} - layout name",
|
||||||
|
"VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
|
||||||
|
private void applyLayout(NamedGraphLayout layout) {
|
||||||
|
currentLayout = layout;
|
||||||
|
layoutButtons.forEach((layoutKey, button)
|
||||||
|
-> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
|
||||||
|
|
||||||
private void organicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_organicLayoutButtonActionPerformed
|
ModalDialogProgressIndicator progressIndicator = new ModalDialogProgressIndicator(windowAncestor, Bundle.VisualizationPanel_computingLayout());
|
||||||
applyOrganicLayout(10);
|
progressIndicator.start(Bundle.VisualizationPanel_computingLayout());
|
||||||
}//GEN-LAST:event_organicLayoutButtonActionPerformed
|
|
||||||
|
|
||||||
private void fastOrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fastOrganicLayoutButtonActionPerformed
|
new SwingWorker<Void, Void>() {
|
||||||
morph(fastOrganicLayout);
|
@Override
|
||||||
}//GEN-LAST:event_fastOrganicLayoutButtonActionPerformed
|
protected Void doInBackground() {
|
||||||
|
graph.getModel().beginUpdate();
|
||||||
|
try {
|
||||||
|
layout.execute(graph.getDefaultParent());
|
||||||
|
fitGraph();
|
||||||
|
} finally {
|
||||||
|
graph.getModel().endUpdate();
|
||||||
|
progressIndicator.finish();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void hierarchyLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_hierarchyLayoutButtonActionPerformed
|
@Override
|
||||||
morph(hierarchicalLayout);
|
protected void done() {
|
||||||
}//GEN-LAST:event_hierarchyLayoutButtonActionPerformed
|
try {
|
||||||
|
get();
|
||||||
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
|
logger.log(Level.WARNING, "CVT graph layout failed.", ex);
|
||||||
|
if (lockedVertexModel.isEmpty()) {
|
||||||
|
MessageNotifyUtil.Message.error(Bundle.VisualizationPanel_layoutFail_text(layout.getDisplayName()));
|
||||||
|
} else {
|
||||||
|
MessageNotifyUtil.Message.error(Bundle.VisualizationPanel_layoutFailWithLockedVertices_text(layout.getDisplayName()));
|
||||||
|
}
|
||||||
|
undoManager.undo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
|
||||||
private void clearVizButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_clearVizButtonActionPerformed
|
private void clearVizButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_clearVizButtonActionPerformed
|
||||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
@ -651,14 +609,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
// Updates the display
|
// Updates the display
|
||||||
graph.getModel().endUpdate();
|
graph.getModel().endUpdate();
|
||||||
setCursor(Cursor.getDefaultCursor());
|
setCursor(Cursor.getDefaultCursor());
|
||||||
|
|
||||||
}//GEN-LAST:event_clearVizButtonActionPerformed
|
}//GEN-LAST:event_clearVizButtonActionPerformed
|
||||||
|
|
||||||
private void applyOrganicLayout(int iterations) {
|
|
||||||
organicLayout.setMaxIterations(iterations);
|
|
||||||
morph(organicLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fitGraph() {
|
private void fitGraph() {
|
||||||
graphComponent.zoomTo(1, true);
|
graphComponent.zoomTo(1, true);
|
||||||
mxPoint translate = graph.getView().getTranslate();
|
mxPoint translate = graph.getView().getTranslate();
|
||||||
@ -681,57 +633,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
|
|
||||||
final Dimension size = graphComponent.getSize();
|
final Dimension size = graphComponent.getSize();
|
||||||
final double widthFactor = size.getWidth() / boundsForCells.getWidth();
|
final double widthFactor = size.getWidth() / boundsForCells.getWidth();
|
||||||
|
final double heightFactor = size.getHeight() / boundsForCells.getHeight();
|
||||||
|
|
||||||
graphComponent.zoom(widthFactor);
|
graphComponent.zoom((heightFactor + widthFactor) / 2.0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void morph(mxIGraphLayout layout) {
|
|
||||||
// layout using morphing
|
|
||||||
graph.getModel().beginUpdate();
|
|
||||||
|
|
||||||
CancelationListener cancelationListener = new CancelationListener();
|
|
||||||
ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Computing layout", new String[]{CANCEL}, CANCEL, cancelationListener);
|
|
||||||
SwingWorker<Void, Void> morphWorker = new SwingWorker<Void, Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground() {
|
|
||||||
progress.start("Computing layout");
|
|
||||||
layout.execute(graph.getDefaultParent());
|
|
||||||
if (isCancelled()) {
|
|
||||||
progress.finish();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20) {
|
|
||||||
@Override
|
|
||||||
public void updateAnimation() {
|
|
||||||
fireEvent(new mxEventObject(mxEvent.EXECUTE));
|
|
||||||
super.updateAnimation(); //To change body of generated methods, choose Tools | Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
morph.addListener(mxEvent.EXECUTE, (Object sender, mxEventObject evt) -> {
|
|
||||||
if (isCancelled()) {
|
|
||||||
morph.stopAnimation();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> {
|
|
||||||
graph.getModel().endUpdate();
|
|
||||||
if (isCancelled()) {
|
|
||||||
undoManager.undo();
|
|
||||||
} else {
|
|
||||||
fitGraph();
|
|
||||||
}
|
|
||||||
progress.finish();
|
|
||||||
});
|
|
||||||
|
|
||||||
morph.startAnimation();
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
cancelationListener.configure(morphWorker, progress);
|
|
||||||
morphWorker.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private JPanel borderLayoutPanel;
|
private JPanel borderLayoutPanel;
|
||||||
@ -755,6 +661,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
private JButton zoomOutButton;
|
private JButton zoomOutButton;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens to graph selection model and updates ExplorerManager to reflect
|
||||||
|
* changes in selection.
|
||||||
|
*/
|
||||||
final private class SelectionListener implements mxEventSource.mxIEventListener {
|
final private class SelectionListener implements mxEventSource.mxIEventListener {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -766,7 +676,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
if (selectionCells.length > 0) {
|
if (selectionCells.length > 0) {
|
||||||
mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]);
|
mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]);
|
||||||
HashSet<Content> relationshipSources = new HashSet<>();
|
HashSet<Content> relationshipSources = new HashSet<>();
|
||||||
HashSet<AccountDeviceInstance> adis = new HashSet<>();
|
HashSet<AccountDeviceInstanceKey> adis = new HashSet<>();
|
||||||
for (mxICell cell : selectedCells) {
|
for (mxICell cell : selectedCells) {
|
||||||
if (cell.isEdge()) {
|
if (cell.isEdge()) {
|
||||||
mxICell source = (mxICell) graph.getModel().getTerminal(cell, true);
|
mxICell source = (mxICell) graph.getModel().getTerminal(cell, true);
|
||||||
@ -783,7 +693,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
logger.log(Level.SEVERE, " Error getting relationsips....", tskCoreException);
|
logger.log(Level.SEVERE, " Error getting relationsips....", tskCoreException);
|
||||||
}
|
}
|
||||||
} else if (cell.isVertex()) {
|
} else if (cell.isVertex()) {
|
||||||
adis.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
|
adis.add((AccountDeviceInstanceKey) cell.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -799,9 +709,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout {
|
/**
|
||||||
|
* Extend mxIGraphLayout with a getDisplayName method,
|
||||||
|
*/
|
||||||
|
private interface NamedGraphLayout extends mxIGraphLayout {
|
||||||
|
|
||||||
mxFastOrganicLayoutImpl(mxGraph graph) {
|
String getDisplayName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension of mxFastOrganicLayout that ignores locked vertices.
|
||||||
|
*/
|
||||||
|
final private class FastOrganicLayoutImpl extends mxFastOrganicLayout implements NamedGraphLayout {
|
||||||
|
|
||||||
|
FastOrganicLayoutImpl(mxGraph graph) {
|
||||||
super(graph);
|
super(graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -812,18 +733,26 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public mxRectangle setVertexLocation(Object vertex, double x, double y) {
|
public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
|
||||||
if (isVertexIgnored(vertex)) {
|
if (isVertexIgnored(vertex)) {
|
||||||
return getVertexBounds(vertex);
|
return getVertexBounds(vertex);
|
||||||
} else {
|
} else {
|
||||||
return super.setVertexLocation(vertex, x, y);
|
return super.setVertexLocation(vertex, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "Fast Organic";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final private class mxCircleLayoutImpl extends mxCircleLayout {
|
/**
|
||||||
|
* Extension of mxCircleLayout that ignores locked vertices.
|
||||||
|
*/
|
||||||
|
final private class CircleLayoutImpl extends mxCircleLayout implements NamedGraphLayout {
|
||||||
|
|
||||||
mxCircleLayoutImpl(mxGraph graph) {
|
CircleLayoutImpl(mxGraph graph) {
|
||||||
super(graph);
|
super(graph);
|
||||||
setResetEdges(true);
|
setResetEdges(true);
|
||||||
}
|
}
|
||||||
@ -835,18 +764,26 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public mxRectangle setVertexLocation(Object vertex, double x, double y) {
|
public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
|
||||||
if (isVertexIgnored(vertex)) {
|
if (isVertexIgnored(vertex)) {
|
||||||
return getVertexBounds(vertex);
|
return getVertexBounds(vertex);
|
||||||
} else {
|
} else {
|
||||||
return super.setVertexLocation(vertex, x, y);
|
return super.setVertexLocation(vertex, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "Circle";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final private class mxOrganicLayoutImpl extends mxOrganicLayout {
|
/**
|
||||||
|
* Extension of mxOrganicLayout that ignores locked vertices.
|
||||||
|
*/
|
||||||
|
final private class OrganicLayoutImpl extends mxOrganicLayout implements NamedGraphLayout {
|
||||||
|
|
||||||
mxOrganicLayoutImpl(mxGraph graph) {
|
OrganicLayoutImpl(mxGraph graph) {
|
||||||
super(graph);
|
super(graph);
|
||||||
setResetEdges(true);
|
setResetEdges(true);
|
||||||
}
|
}
|
||||||
@ -858,18 +795,26 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public mxRectangle setVertexLocation(Object vertex, double x, double y) {
|
public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
|
||||||
if (isVertexIgnored(vertex)) {
|
if (isVertexIgnored(vertex)) {
|
||||||
return getVertexBounds(vertex);
|
return getVertexBounds(vertex);
|
||||||
} else {
|
} else {
|
||||||
return super.setVertexLocation(vertex, x, y);
|
return super.setVertexLocation(vertex, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "Organic";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final private class mxHierarchicalLayoutImpl extends mxHierarchicalLayout {
|
/**
|
||||||
|
* Extension of mxHierarchicalLayout that ignores locked vertices.
|
||||||
|
*/
|
||||||
|
final private class HierarchicalLayoutImpl extends mxHierarchicalLayout implements NamedGraphLayout {
|
||||||
|
|
||||||
mxHierarchicalLayoutImpl(mxGraph graph) {
|
HierarchicalLayoutImpl(mxGraph graph) {
|
||||||
super(graph);
|
super(graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -880,15 +825,24 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public mxRectangle setVertexLocation(Object vertex, double x, double y) {
|
public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
|
||||||
if (isVertexIgnored(vertex)) {
|
if (isVertexIgnored(vertex)) {
|
||||||
return getVertexBounds(vertex);
|
return getVertexBounds(vertex);
|
||||||
} else {
|
} else {
|
||||||
return super.setVertexLocation(vertex, x, y);
|
return super.setVertexLocation(vertex, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "Hierarchical";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener that closses the given ModalDialogProgressIndicator and cancels
|
||||||
|
* the future.
|
||||||
|
*/
|
||||||
private class CancelationListener implements ActionListener {
|
private class CancelationListener implements ActionListener {
|
||||||
|
|
||||||
private Future<?> cancellable;
|
private Future<?> cancellable;
|
||||||
@ -905,6 +859,108 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
|||||||
cancellable.cancel(true);
|
cancellable.cancel(true);
|
||||||
progress.finish();
|
progress.finish();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mouse Adapter for the graphComponent. Handles wheel zooming and context
|
||||||
|
* menus.
|
||||||
|
*/
|
||||||
|
private class GraphMouseListener extends MouseAdapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate mouse wheel events into zooming.
|
||||||
|
*
|
||||||
|
* @param event The MouseWheelEvent
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void mouseWheelMoved(final MouseWheelEvent event) {
|
||||||
|
super.mouseWheelMoved(event);
|
||||||
|
if (event.getPreciseWheelRotation() < 0) {
|
||||||
|
graphComponent.zoomIn();
|
||||||
|
} else if (event.getPreciseWheelRotation() > 0) {
|
||||||
|
graphComponent.zoomOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right click handler: show context menu.
|
||||||
|
*
|
||||||
|
* @param event The MouseEvent
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(final MouseEvent event) {
|
||||||
|
super.mouseClicked(event);
|
||||||
|
if (SwingUtilities.isRightMouseButton(event)) {
|
||||||
|
final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(), event.getY());
|
||||||
|
if (cellAt != null && cellAt.isVertex()) {
|
||||||
|
final JPopupMenu jPopupMenu = new JPopupMenu();
|
||||||
|
final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
|
||||||
|
|
||||||
|
Set<mxCell> selectedVertices
|
||||||
|
= Stream.of(graph.getSelectionModel().getCells())
|
||||||
|
.map(mxCell.class::cast)
|
||||||
|
.filter(mxCell::isVertex)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
if (lockedVertexModel.isVertexLocked(cellAt)) {
|
||||||
|
jPopupMenu.add(new JMenuItem(new UnlockAction(selectedVertices)));
|
||||||
|
} else {
|
||||||
|
jPopupMenu.add(new JMenuItem(new LockAction(selectedVertices)));
|
||||||
|
}
|
||||||
|
if (pinnedAccountModel.isAccountPinned(adiKey)) {
|
||||||
|
jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
|
||||||
|
} else {
|
||||||
|
jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
|
||||||
|
jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
|
||||||
|
}
|
||||||
|
jPopupMenu.show(graphComponent.getGraphControl(), event.getX(), event.getY());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action that un-locks the selected vertices.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
|
||||||
|
"VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
|
||||||
|
private final class UnlockAction extends AbstractAction {
|
||||||
|
|
||||||
|
private final Set<mxCell> selectedVertices;
|
||||||
|
|
||||||
|
UnlockAction(Set<mxCell> selectedVertices) {
|
||||||
|
super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
|
||||||
|
unlockIcon);
|
||||||
|
this.selectedVertices = selectedVertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
|
||||||
|
public void actionPerformed(final ActionEvent event) {
|
||||||
|
lockedVertexModel.unlock(selectedVertices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action that locks the selected vertices.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"VisualizationPanel.lockAction.singularText=Lock Selected Account",
|
||||||
|
"VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
|
||||||
|
private final class LockAction extends AbstractAction {
|
||||||
|
|
||||||
|
private final Set<mxCell> selectedVertices;
|
||||||
|
|
||||||
|
LockAction(Set<mxCell> selectedVertices) {
|
||||||
|
super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
|
||||||
|
lockIcon);
|
||||||
|
this.selectedVertices = selectedVertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(final ActionEvent event) {
|
||||||
|
lockedVertexModel.lock(selectedVertices);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -503,7 +503,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
|
|||||||
rtfbodyTextPane.setText("");
|
rtfbodyTextPane.setText("");
|
||||||
htmlbodyTextPane.setText("");
|
htmlbodyTextPane.setText("");
|
||||||
textbodyTextArea.setText("");
|
textbodyTextArea.setText("");
|
||||||
drp.setNode(null);
|
|
||||||
showImagesToggleButton.setEnabled(false);
|
showImagesToggleButton.setEnabled(false);
|
||||||
msgbodyTabbedPane.setEnabled(false);
|
msgbodyTabbedPane.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ DataContentViewerString.pageLabel2.text=Page
|
|||||||
# Product Information panel
|
# Product Information panel
|
||||||
LBL_Description=<div style=\"font-size: 12pt; font-family: Verdana, 'Verdana CE', Arial, 'Arial CE', 'Lucida Grande CE', lucida, 'Helvetica CE', sans-serif;\">\n <b>Product Version:</b> {0} ({9}) <br><b>Sleuth Kit Version:</b> {7} <br><b>Netbeans RCP Build:</b> {8} <br> <b>Java:</b> {1}; {2}<br> <b>System:</b> {3}; {4}; {5}<br><b>Userdir:</b> {6}</div>
|
LBL_Description=<div style=\"font-size: 12pt; font-family: Verdana, 'Verdana CE', Arial, 'Arial CE', 'Lucida Grande CE', lucida, 'Helvetica CE', sans-serif;\">\n <b>Product Version:</b> {0} ({9}) <br><b>Sleuth Kit Version:</b> {7} <br><b>Netbeans RCP Build:</b> {8} <br> <b>Java:</b> {1}; {2}<br> <b>System:</b> {3}; {4}; {5}<br><b>Userdir:</b> {6}</div>
|
||||||
Format_OperatingSystem_Value={0} version {1} running on {2}
|
Format_OperatingSystem_Value={0} version {1} running on {2}
|
||||||
LBL_Copyright=<div style\="font-size\: 12pt; font-family\: Verdana, 'Verdana CE', Arial, 'Arial CE', 'Lucida Grande CE', lucida, 'Helvetica CE', sans-serif; ">Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools. <br><ul><li>General Information: <a style\="color\: \#1E2A60;" href\="http\://www.sleuthkit.org">http\://www.sleuthkit.org</a>.</li><li>Training: <a style\="color\: \#1E2A60;" href\="http://www.basistech.com/autopsy-training">http://www.basistech.com/autopsy-training</a></li><li>Commercial Support: <a style\="color\: \#1E2A60;" href\="http://www.basistech.com/digital-forensics/autopsy/support/">http://www.basistech.com/digital-forensics/autopsy/support/</a></li></ul>Copyright © 2003-2017. </div>
|
LBL_Copyright=<div style\="font-size\: 12pt; font-family\: Verdana, 'Verdana CE', Arial, 'Arial CE', 'Lucida Grande CE', lucida, 'Helvetica CE', sans-serif; ">Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools. <br><ul><li>General Information: <a style\="color\: \#1E2A60;" href\="http\://www.sleuthkit.org">http\://www.sleuthkit.org</a>.</li><li>Training: <a style\="color\: \#1E2A60;" href\="http://www.basistech.com/autopsy-training">http://www.basistech.com/autopsy-training</a></li><li>Commercial Support: <a style\="color\: \#1E2A60;" href\="http://www.basistech.com/digital-forensics/autopsy/support/">http://www.basistech.com/digital-forensics/autopsy/support/</a></li></ul>Copyright © 2003-2018. </div>
|
||||||
URL_ON_IMG=http://www.sleuthkit.org/
|
URL_ON_IMG=http://www.sleuthkit.org/
|
||||||
URL_ON_HELP=http://sleuthkit.org/autopsy/docs/user-docs/4.6.0/
|
URL_ON_HELP=http://sleuthkit.org/autopsy/docs/user-docs/4.6.0/
|
||||||
FILE_FOR_LOCAL_HELP=file:///
|
FILE_FOR_LOCAL_HELP=file:///
|
||||||
|
@ -408,6 +408,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
|||||||
protected void addNotify() {
|
protected void addNotify() {
|
||||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||||
|
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||||
super.addNotify();
|
super.addNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,6 +416,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
|||||||
protected void removeNotify() {
|
protected void removeNotify() {
|
||||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
||||||
|
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||||
super.removeNotify();
|
super.removeNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,6 +571,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
|||||||
protected void addNotify() {
|
protected void addNotify() {
|
||||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||||
|
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||||
super.addNotify();
|
super.addNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,6 +579,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
|||||||
protected void removeNotify() {
|
protected void removeNotify() {
|
||||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
||||||
|
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||||
super.removeNotify();
|
super.removeNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,6 +695,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
|||||||
protected void addNotify() {
|
protected void addNotify() {
|
||||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||||
|
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||||
super.addNotify();
|
super.addNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,6 +703,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
|||||||
protected void removeNotify() {
|
protected void removeNotify() {
|
||||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
||||||
|
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||||
super.removeNotify();
|
super.removeNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,6 +907,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
|||||||
protected void addNotify() {
|
protected void addNotify() {
|
||||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||||
|
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||||
super.addNotify();
|
super.addNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -908,6 +915,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
|||||||
protected void removeNotify() {
|
protected void removeNotify() {
|
||||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
||||||
|
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||||
super.removeNotify();
|
super.removeNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,3 +18,4 @@ TestPanel.jLabel5.text=Number of nodes
|
|||||||
TestPanel.nNodesTextField.text=
|
TestPanel.nNodesTextField.text=
|
||||||
TestPanel.jButton2.text=Populate DB
|
TestPanel.jButton2.text=Populate DB
|
||||||
TestPanel.newGraphButton.text=New graph!
|
TestPanel.newGraphButton.text=New graph!
|
||||||
|
TestPanel.jButton3.text=Make blocky data
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
package org.sleuthkit.autopsy.healthmonitor;
|
package org.sleuthkit.autopsy.healthmonitor;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
@ -29,17 +31,24 @@ import java.util.Map;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferencesException;
|
import org.sleuthkit.autopsy.core.UserPreferencesException;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.ThreadUtils;
|
||||||
import org.sleuthkit.datamodel.CaseDbConnectionInfo;
|
import org.sleuthkit.datamodel.CaseDbConnectionInfo;
|
||||||
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
|
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
|
||||||
|
|
||||||
@ -50,30 +59,46 @@ import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
|
|||||||
* Modules will call getTimingMetric() before the code to be timed to get a TimingMetric object
|
* Modules will call getTimingMetric() before the code to be timed to get a TimingMetric object
|
||||||
* Modules will call submitTimingMetric() with the obtained TimingMetric object to log it
|
* Modules will call submitTimingMetric() with the obtained TimingMetric object to log it
|
||||||
*/
|
*/
|
||||||
public class ServicesHealthMonitor {
|
public final class EnterpriseHealthMonitor implements PropertyChangeListener {
|
||||||
|
|
||||||
private final static Logger logger = Logger.getLogger(ServicesHealthMonitor.class.getName());
|
private final static Logger logger = Logger.getLogger(EnterpriseHealthMonitor.class.getName());
|
||||||
private final static String DATABASE_NAME = "ServicesHealthMonitor";
|
private final static String DATABASE_NAME = "EnterpriseHealthMonitor";
|
||||||
private final static String MODULE_NAME = "ServicesHealthMonitor";
|
private final static String MODULE_NAME = "EnterpriseHealthMonitor";
|
||||||
private final static String IS_ENABLED_KEY = "is_enabled";
|
private final static String IS_ENABLED_KEY = "is_enabled";
|
||||||
private final static long DATABASE_WRITE_INTERVAL = 1; // Minutes TODO - put back to an hour
|
private final static long DATABASE_WRITE_INTERVAL = 1; // Minutes TODO - put back to an hour
|
||||||
public static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION
|
public static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION
|
||||||
= new CaseDbSchemaVersionNumber(1, 0);
|
= new CaseDbSchemaVersionNumber(1, 0);
|
||||||
|
|
||||||
private static final AtomicBoolean isEnabled = new AtomicBoolean(false);
|
private static final AtomicBoolean isEnabled = new AtomicBoolean(false);
|
||||||
private static ServicesHealthMonitor instance;
|
private static EnterpriseHealthMonitor instance;
|
||||||
|
|
||||||
private ScheduledThreadPoolExecutor periodicTasksExecutor;
|
private final ExecutorService healthMonitorExecutor;
|
||||||
|
private static final String HEALTH_MONITOR_EVENT_THREAD_NAME = "Health-Monitor-Event-Listener-%d";
|
||||||
|
|
||||||
|
private ScheduledThreadPoolExecutor healthMonitorOutputTimer;
|
||||||
private final Map<String, TimingInfo> timingInfoMap;
|
private final Map<String, TimingInfo> timingInfoMap;
|
||||||
private static final int CONN_POOL_SIZE = 10;
|
private static final int CONN_POOL_SIZE = 10;
|
||||||
private BasicDataSource connectionPool = null;
|
private BasicDataSource connectionPool = null;
|
||||||
|
private String hostName;
|
||||||
|
|
||||||
private ServicesHealthMonitor() throws HealthMonitorException {
|
private EnterpriseHealthMonitor() throws HealthMonitorException {
|
||||||
|
|
||||||
// Create the map to collect timing metrics. The map will exist regardless
|
// Create the map to collect timing metrics. The map will exist regardless
|
||||||
// of whether the monitor is enabled.
|
// of whether the monitor is enabled.
|
||||||
timingInfoMap = new HashMap<>();
|
timingInfoMap = new HashMap<>();
|
||||||
|
|
||||||
|
// Set up the executor to handle case events
|
||||||
|
healthMonitorExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(HEALTH_MONITOR_EVENT_THREAD_NAME).build());
|
||||||
|
|
||||||
|
// Get the host name
|
||||||
|
try {
|
||||||
|
hostName = java.net.InetAddress.getLocalHost().getHostName();
|
||||||
|
} catch (java.net.UnknownHostException ex) {
|
||||||
|
// Continue on, but log the error and generate a UUID to use for this session
|
||||||
|
hostName = UUID.randomUUID().toString();
|
||||||
|
logger.log(Level.SEVERE, "Unable to look up host name - falling back to UUID " + hostName, ex);
|
||||||
|
}
|
||||||
|
|
||||||
// Read from module settings to determine if the module is enabled
|
// Read from module settings to determine if the module is enabled
|
||||||
if (ModuleSettings.settingExists(MODULE_NAME, IS_ENABLED_KEY)) {
|
if (ModuleSettings.settingExists(MODULE_NAME, IS_ENABLED_KEY)) {
|
||||||
if(ModuleSettings.getConfigSetting(MODULE_NAME, IS_ENABLED_KEY).equals("true")){
|
if(ModuleSettings.getConfigSetting(MODULE_NAME, IS_ENABLED_KEY).equals("true")){
|
||||||
@ -93,13 +118,14 @@ public class ServicesHealthMonitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the instance of the ServicesHealthMonitor
|
* Get the instance of the EnterpriseHealthMonitor
|
||||||
* @return the instance
|
* @return the instance
|
||||||
* @throws HealthMonitorException
|
* @throws HealthMonitorException
|
||||||
*/
|
*/
|
||||||
synchronized static ServicesHealthMonitor getInstance() throws HealthMonitorException {
|
synchronized static EnterpriseHealthMonitor getInstance() throws HealthMonitorException {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new ServicesHealthMonitor();
|
instance = new EnterpriseHealthMonitor();
|
||||||
|
Case.addPropertyChangeListener(instance);
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
@ -118,15 +144,15 @@ public class ServicesHealthMonitor {
|
|||||||
shutdownConnections();
|
shutdownConnections();
|
||||||
|
|
||||||
if (!UserPreferences.getIsMultiUserModeEnabled()) {
|
if (!UserPreferences.getIsMultiUserModeEnabled()) {
|
||||||
throw new HealthMonitorException("Multi user mode is not enabled - can not activate services health monitor");
|
throw new HealthMonitorException("Multi user mode is not enabled - can not activate health monitor");
|
||||||
}
|
|
||||||
// Set up database (if needed)
|
|
||||||
CoordinationService.Lock lock = getExclusiveDbLock();
|
|
||||||
if(lock == null) {
|
|
||||||
throw new HealthMonitorException("Error getting database lock");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// Set up database (if needed)
|
||||||
|
try (CoordinationService.Lock lock = getExclusiveDbLock()) {
|
||||||
|
if(lock == null) {
|
||||||
|
throw new HealthMonitorException("Error getting database lock");
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the database exists
|
// Check if the database exists
|
||||||
if (! databaseExists()) {
|
if (! databaseExists()) {
|
||||||
|
|
||||||
@ -138,12 +164,8 @@ public class ServicesHealthMonitor {
|
|||||||
initializeDatabaseSchema();
|
initializeDatabaseSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} catch (CoordinationService.CoordinationServiceException ex) {
|
||||||
try {
|
throw new HealthMonitorException("Error releasing database lock", ex);
|
||||||
lock.release();
|
|
||||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
|
||||||
throw new HealthMonitorException("Error releasing database lock", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear out any old data
|
// Clear out any old data
|
||||||
@ -178,20 +200,19 @@ public class ServicesHealthMonitor {
|
|||||||
* Start the ScheduledThreadPoolExecutor that will handle the database writes.
|
* Start the ScheduledThreadPoolExecutor that will handle the database writes.
|
||||||
*/
|
*/
|
||||||
private synchronized void startTimer() {
|
private synchronized void startTimer() {
|
||||||
if(periodicTasksExecutor != null) {
|
// Make sure the previous executor (if it exists) has been stopped
|
||||||
// Make sure the previous executor (if it exists) has been stopped
|
stopTimer();
|
||||||
periodicTasksExecutor.shutdown();
|
|
||||||
}
|
healthMonitorOutputTimer = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("health_monitor_timer").build());
|
||||||
periodicTasksExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("health_monitor_timer").build());
|
healthMonitorOutputTimer.scheduleWithFixedDelay(new DatabaseWriteTask(), DATABASE_WRITE_INTERVAL, DATABASE_WRITE_INTERVAL, TimeUnit.MINUTES);
|
||||||
periodicTasksExecutor.scheduleWithFixedDelay(new DatabaseWriteTask(), DATABASE_WRITE_INTERVAL, DATABASE_WRITE_INTERVAL, TimeUnit.MINUTES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the ScheduledThreadPoolExecutor to prevent further database writes.
|
* Stop the ScheduledThreadPoolExecutor to prevent further database writes.
|
||||||
*/
|
*/
|
||||||
private synchronized void stopTimer() {
|
private synchronized void stopTimer() {
|
||||||
if(periodicTasksExecutor != null) {
|
if(healthMonitorOutputTimer != null) {
|
||||||
periodicTasksExecutor.shutdown();
|
ThreadUtils.shutDownTaskExecutor(healthMonitorOutputTimer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +220,7 @@ public class ServicesHealthMonitor {
|
|||||||
* Called from the installer to set up the Health Monitor instance at startup.
|
* Called from the installer to set up the Health Monitor instance at startup.
|
||||||
* @throws HealthMonitorException
|
* @throws HealthMonitorException
|
||||||
*/
|
*/
|
||||||
static synchronized void startUp() throws HealthMonitorException {
|
static synchronized void startUpIfEnabled() throws HealthMonitorException {
|
||||||
getInstance();
|
getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +252,7 @@ public class ServicesHealthMonitor {
|
|||||||
* Get a metric that will measure the time to execute a section of code.
|
* Get a metric that will measure the time to execute a section of code.
|
||||||
* Call this before the section of code to be timed and then
|
* Call this before the section of code to be timed and then
|
||||||
* submit it afterward using submitTimingMetric().
|
* submit it afterward using submitTimingMetric().
|
||||||
* This method is safe to call regardless of whether the Services Health
|
* This method is safe to call regardless of whether the Enterprise Health
|
||||||
* Monitor is enabled.
|
* Monitor is enabled.
|
||||||
* @param name A short but descriptive name describing the code being timed.
|
* @param name A short but descriptive name describing the code being timed.
|
||||||
* This name will appear in the UI.
|
* This name will appear in the UI.
|
||||||
@ -247,7 +268,7 @@ public class ServicesHealthMonitor {
|
|||||||
/**
|
/**
|
||||||
* Submit the metric that was previously obtained through getTimingMetric().
|
* Submit the metric that was previously obtained through getTimingMetric().
|
||||||
* Call this immediately after the section of code being timed.
|
* Call this immediately after the section of code being timed.
|
||||||
* This method is safe to call regardless of whether the Services Health
|
* This method is safe to call regardless of whether the Enterprise Health
|
||||||
* Monitor is enabled.
|
* Monitor is enabled.
|
||||||
* @param metric The TimingMetric object obtained from getTimingMetric()
|
* @param metric The TimingMetric object obtained from getTimingMetric()
|
||||||
*/
|
*/
|
||||||
@ -306,29 +327,20 @@ public class ServicesHealthMonitor {
|
|||||||
timingMapCopy = new HashMap<>(timingInfoMap);
|
timingMapCopy = new HashMap<>(timingInfoMap);
|
||||||
timingInfoMap.clear();
|
timingInfoMap.clear();
|
||||||
}
|
}
|
||||||
logger.log(Level.INFO, "Writing health monitor metrics to database");
|
|
||||||
|
|
||||||
String hostName;
|
|
||||||
try {
|
|
||||||
hostName = java.net.InetAddress.getLocalHost().getHostName();
|
|
||||||
} catch (java.net.UnknownHostException ex) {
|
|
||||||
// Write it to the database but log a warning
|
|
||||||
logger.log(Level.WARNING, "Unable to look up host name");
|
|
||||||
hostName = "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there's anything to report (right now we only have the timing map)
|
// Check if there's anything to report (right now we only have the timing map)
|
||||||
if(timingMapCopy.keySet().isEmpty()) {
|
if(timingMapCopy.keySet().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to the database
|
logger.log(Level.INFO, "Writing health monitor metrics to database");
|
||||||
CoordinationService.Lock lock = getSharedDbLock();
|
|
||||||
if(lock == null) {
|
|
||||||
throw new HealthMonitorException("Error getting database lock");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
// Write to the database
|
||||||
|
try (CoordinationService.Lock lock = getSharedDbLock()) {
|
||||||
|
if(lock == null) {
|
||||||
|
throw new HealthMonitorException("Error getting database lock");
|
||||||
|
}
|
||||||
|
|
||||||
Connection conn = connect();
|
Connection conn = connect();
|
||||||
if(conn == null) {
|
if(conn == null) {
|
||||||
throw new HealthMonitorException("Error getting database connection");
|
throw new HealthMonitorException("Error getting database connection");
|
||||||
@ -361,12 +373,8 @@ public class ServicesHealthMonitor {
|
|||||||
logger.log(Level.SEVERE, "Error closing Connection.", ex);
|
logger.log(Level.SEVERE, "Error closing Connection.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} catch (CoordinationService.CoordinationServiceException ex) {
|
||||||
try {
|
throw new HealthMonitorException("Error releasing database lock", ex);
|
||||||
lock.release();
|
|
||||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
|
||||||
throw new HealthMonitorException("Error releasing database lock", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,10 +393,9 @@ public class ServicesHealthMonitor {
|
|||||||
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + db.getHost() + ":" + db.getPort() + "/postgres", db.getUserName(), db.getPassword()); //NON-NLS
|
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + db.getHost() + ":" + db.getPort() + "/postgres", db.getUserName(), db.getPassword()); //NON-NLS
|
||||||
Statement statement = connection.createStatement();) {
|
Statement statement = connection.createStatement();) {
|
||||||
String createCommand = "SELECT 1 AS result FROM pg_database WHERE datname='" + DATABASE_NAME + "'";
|
String createCommand = "SELECT 1 AS result FROM pg_database WHERE datname='" + DATABASE_NAME + "'";
|
||||||
System.out.println(" query: " + createCommand);
|
|
||||||
rs = statement.executeQuery(createCommand);
|
rs = statement.executeQuery(createCommand);
|
||||||
if(rs.next()) {
|
if(rs.next()) {
|
||||||
logger.log(Level.INFO, "Existing Services Health Monitor database found");
|
logger.log(Level.INFO, "Existing Enterprise Health Monitor database found");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -593,26 +600,26 @@ public class ServicesHealthMonitor {
|
|||||||
try (Statement statement = conn.createStatement()) {
|
try (Statement statement = conn.createStatement()) {
|
||||||
conn.setAutoCommit(false);
|
conn.setAutoCommit(false);
|
||||||
|
|
||||||
StringBuilder createTimingTable = new StringBuilder();
|
String createTimingTable =
|
||||||
createTimingTable.append("CREATE TABLE IF NOT EXISTS timing_data (");
|
"CREATE TABLE IF NOT EXISTS timing_data (" +
|
||||||
createTimingTable.append("id SERIAL PRIMARY KEY,");
|
"id SERIAL PRIMARY KEY," +
|
||||||
createTimingTable.append("name text NOT NULL,");
|
"name text NOT NULL," +
|
||||||
createTimingTable.append("host text NOT NULL,");
|
"host text NOT NULL," +
|
||||||
createTimingTable.append("timestamp bigint NOT NULL,");
|
"timestamp bigint NOT NULL," +
|
||||||
createTimingTable.append("count bigint NOT NULL,");
|
"count bigint NOT NULL," +
|
||||||
createTimingTable.append("average bigint NOT NULL,");
|
"average bigint NOT NULL," +
|
||||||
createTimingTable.append("max bigint NOT NULL,");
|
"max bigint NOT NULL," +
|
||||||
createTimingTable.append("min bigint NOT NULL");
|
"min bigint NOT NULL" +
|
||||||
createTimingTable.append(")");
|
")";
|
||||||
statement.execute(createTimingTable.toString());
|
statement.execute(createTimingTable);
|
||||||
|
|
||||||
StringBuilder createDbInfoTable = new StringBuilder();
|
String createDbInfoTable =
|
||||||
createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info (");
|
"CREATE TABLE IF NOT EXISTS db_info (" +
|
||||||
createDbInfoTable.append("id SERIAL PRIMARY KEY NOT NULL,");
|
"id SERIAL PRIMARY KEY NOT NULL," +
|
||||||
createDbInfoTable.append("name text NOT NULL,");
|
"name text NOT NULL," +
|
||||||
createDbInfoTable.append("value text NOT NULL");
|
"value text NOT NULL" +
|
||||||
createDbInfoTable.append(")");
|
")";
|
||||||
statement.execute(createDbInfoTable.toString());
|
statement.execute(createDbInfoTable);
|
||||||
|
|
||||||
statement.execute("INSERT INTO db_info (name, value) VALUES ('SCHEMA_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')");
|
statement.execute("INSERT INTO db_info (name, value) VALUES ('SCHEMA_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')");
|
||||||
statement.execute("INSERT INTO db_info (name, value) VALUES ('SCHEMA_MINOR_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "')");
|
statement.execute("INSERT INTO db_info (name, value) VALUES ('SCHEMA_MINOR_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "')");
|
||||||
@ -653,11 +660,25 @@ public class ServicesHealthMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
|
||||||
|
switch (Case.Events.valueOf(evt.getPropertyName())) {
|
||||||
|
|
||||||
|
case CURRENT_CASE:
|
||||||
|
if ((null == evt.getNewValue()) && (evt.getOldValue() instanceof Case)) {
|
||||||
|
// When a case is closed, write the current metrics to the database
|
||||||
|
healthMonitorExecutor.submit(new EnterpriseHealthMonitor.DatabaseWriteTask());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: remove this - testing only
|
* TODO: remove this - testing only
|
||||||
* Will put a bunch of sample data into the database
|
* Will put a bunch of sample data into the database
|
||||||
*/
|
*/
|
||||||
final void populateDatabase(int nDays, int nNodes) throws HealthMonitorException {
|
final void populateDatabase(int nDays, int nNodes, boolean createVerificationData) throws HealthMonitorException {
|
||||||
|
|
||||||
if(! isEnabled.get()) {
|
if(! isEnabled.get()) {
|
||||||
throw new HealthMonitorException("Can't populate database - monitor not enabled");
|
throw new HealthMonitorException("Can't populate database - monitor not enabled");
|
||||||
@ -708,16 +729,27 @@ public class ServicesHealthMonitor {
|
|||||||
|
|
||||||
long aveTime;
|
long aveTime;
|
||||||
|
|
||||||
// Make different cases
|
if( ! createVerificationData ) {
|
||||||
int outlierVal = rand.nextInt(30);
|
// Try to make a reasonable sample data set, with most points in a small range
|
||||||
if(outlierVal < 2){
|
// but some higher and lower
|
||||||
aveTime = minIndexTime + maxIndexTimeOverMin + rand.nextInt(maxIndexTimeOverMin);
|
int outlierVal = rand.nextInt(30);
|
||||||
} else if(outlierVal == 2){
|
if(outlierVal < 2){
|
||||||
aveTime = (minIndexTime / 2) + rand.nextInt(minIndexTime / 2);
|
aveTime = minIndexTime + maxIndexTimeOverMin + rand.nextInt(maxIndexTimeOverMin);
|
||||||
} else if(outlierVal < 17) {
|
} else if(outlierVal == 2){
|
||||||
aveTime = minIndexTime + (rand.nextInt(maxIndexTimeOverMin) / 2);
|
aveTime = (minIndexTime / 2) + rand.nextInt(minIndexTime / 2);
|
||||||
|
} else if(outlierVal < 17) {
|
||||||
|
aveTime = minIndexTime + (rand.nextInt(maxIndexTimeOverMin) / 2);
|
||||||
|
} else {
|
||||||
|
aveTime = minIndexTime + rand.nextInt(maxIndexTimeOverMin);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
aveTime = minIndexTime + rand.nextInt(maxIndexTimeOverMin);
|
// Create a data set strictly for testing that the display is working
|
||||||
|
// correctly. The average time will equal the day of the month from
|
||||||
|
// the timestamp (in milliseconds)
|
||||||
|
Calendar thisDate = new GregorianCalendar();
|
||||||
|
thisDate.setTimeInMillis(timestamp);
|
||||||
|
int day = thisDate.get(Calendar.DAY_OF_MONTH);
|
||||||
|
aveTime = day * 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -736,14 +768,27 @@ public class ServicesHealthMonitor {
|
|||||||
// Record index chunk every hour
|
// Record index chunk every hour
|
||||||
for(long timestamp = minTimestamp + rand.nextInt(1000 * 60 * 55);timestamp < maxTimestamp;timestamp += (1 + rand.nextInt(10)) * millisPerHour) {
|
for(long timestamp = minTimestamp + rand.nextInt(1000 * 60 * 55);timestamp < maxTimestamp;timestamp += (1 + rand.nextInt(10)) * millisPerHour) {
|
||||||
|
|
||||||
long aveTime = minConnTime + rand.nextInt(maxConnTimeOverMin);
|
long aveTime;
|
||||||
|
if( ! createVerificationData ) {
|
||||||
// Check if we should make an outlier
|
// Try to make a reasonable sample data set, with most points in a small range
|
||||||
int outlierVal = rand.nextInt(30);
|
// but some higher and lower
|
||||||
if(outlierVal < 2){
|
aveTime = minConnTime + rand.nextInt(maxConnTimeOverMin);
|
||||||
aveTime = minConnTime + maxConnTimeOverMin + rand.nextInt(maxConnTimeOverMin);
|
|
||||||
} else if(outlierVal == 8){
|
// Check if we should make an outlier
|
||||||
aveTime = (minConnTime / 2) + rand.nextInt(minConnTime / 2);
|
int outlierVal = rand.nextInt(30);
|
||||||
|
if(outlierVal < 2){
|
||||||
|
aveTime = minConnTime + maxConnTimeOverMin + rand.nextInt(maxConnTimeOverMin);
|
||||||
|
} else if(outlierVal == 8){
|
||||||
|
aveTime = (minConnTime / 2) + rand.nextInt(minConnTime / 2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create a data set strictly for testing that the display is working
|
||||||
|
// correctly. The average time will equal the day of the month from
|
||||||
|
// the timestamp (in milliseconds)
|
||||||
|
Calendar thisDate = new GregorianCalendar();
|
||||||
|
thisDate.setTimeInMillis(timestamp);
|
||||||
|
int day = thisDate.get(Calendar.DAY_OF_MONTH);
|
||||||
|
aveTime = day * 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
statement.setString(1, "Solr: Connectivity check");
|
statement.setString(1, "Solr: Connectivity check");
|
||||||
@ -857,15 +902,14 @@ public class ServicesHealthMonitor {
|
|||||||
*/
|
*/
|
||||||
private CoordinationService.Lock getExclusiveDbLock() throws HealthMonitorException{
|
private CoordinationService.Lock getExclusiveDbLock() throws HealthMonitorException{
|
||||||
try {
|
try {
|
||||||
String databaseNodeName = DATABASE_NAME;
|
CoordinationService.Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CoordinationService.CategoryNode.HEALTH_MONITOR, DATABASE_NAME, 5, TimeUnit.MINUTES);
|
||||||
CoordinationService.Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CoordinationService.CategoryNode.HEALTH_MONITOR, databaseNodeName, 5, TimeUnit.MINUTES);
|
|
||||||
|
|
||||||
if(lock != null){
|
if(lock != null){
|
||||||
return lock;
|
return lock;
|
||||||
}
|
}
|
||||||
throw new HealthMonitorException("Error acquiring database lock");
|
throw new HealthMonitorException("Error acquiring database lock");
|
||||||
} catch (InterruptedException | CoordinationService.CoordinationServiceException ex){
|
} catch (InterruptedException | CoordinationService.CoordinationServiceException ex){
|
||||||
throw new HealthMonitorException("Error acquiring database lock");
|
throw new HealthMonitorException("Error acquiring database lock", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -973,14 +1017,16 @@ public class ServicesHealthMonitor {
|
|||||||
*/
|
*/
|
||||||
static class DatabaseTimingResult {
|
static class DatabaseTimingResult {
|
||||||
private long timestamp; // Time the metric was recorded
|
private long timestamp; // Time the metric was recorded
|
||||||
|
private String hostname; // Host that recorded the metric
|
||||||
private long count; // Number of metrics collected
|
private long count; // Number of metrics collected
|
||||||
private double average; // Average of the durations collected (milliseconds)
|
private double average; // Average of the durations collected (milliseconds)
|
||||||
private double max; // Maximum value found (milliseconds)
|
private double max; // Maximum value found (milliseconds)
|
||||||
private double min; // Minimum value found (milliseconds)
|
private double min; // Minimum value found (milliseconds)
|
||||||
|
|
||||||
// TODO - maybe delete this
|
// TODO - maybe delete this
|
||||||
DatabaseTimingResult(long timestamp, long count, double average, double max, double min) {
|
DatabaseTimingResult(long timestamp, String hostname, long count, double average, double max, double min) {
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
|
this.hostname = hostname;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
this.average = average;
|
this.average = average;
|
||||||
this.max = max;
|
this.max = max;
|
||||||
@ -989,6 +1035,7 @@ public class ServicesHealthMonitor {
|
|||||||
|
|
||||||
DatabaseTimingResult(ResultSet resultSet) throws SQLException {
|
DatabaseTimingResult(ResultSet resultSet) throws SQLException {
|
||||||
this.timestamp = resultSet.getLong("timestamp");
|
this.timestamp = resultSet.getLong("timestamp");
|
||||||
|
this.hostname = resultSet.getString("host");
|
||||||
this.count = resultSet.getLong("count");
|
this.count = resultSet.getLong("count");
|
||||||
this.average = resultSet.getLong("average") / 1000000;
|
this.average = resultSet.getLong("average") / 1000000;
|
||||||
this.max = resultSet.getLong("max") / 1000000;
|
this.max = resultSet.getLong("max") / 1000000;
|
||||||
@ -1034,5 +1081,13 @@ public class ServicesHealthMonitor {
|
|||||||
long getCount() {
|
long getCount() {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the host that recorded this metric
|
||||||
|
* @return the host
|
||||||
|
*/
|
||||||
|
String getHostName() {
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.healthmonitor;
|
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
||||||
import java.beans.PropertyChangeEvent;
|
|
||||||
import java.beans.PropertyChangeListener;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener for case events
|
|
||||||
*/
|
|
||||||
final class HealthMonitorCaseEventListener implements PropertyChangeListener {
|
|
||||||
|
|
||||||
private final ExecutorService jobProcessingExecutor;
|
|
||||||
private static final String CASE_EVENT_THREAD_NAME = "Health-Monitor-Event-Listener-%d";
|
|
||||||
|
|
||||||
HealthMonitorCaseEventListener() {
|
|
||||||
jobProcessingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(CASE_EVENT_THREAD_NAME).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
|
||||||
|
|
||||||
switch (Case.Events.valueOf(evt.getPropertyName())) {
|
|
||||||
|
|
||||||
case CURRENT_CASE:
|
|
||||||
if ((null == evt.getNewValue()) && (evt.getOldValue() instanceof Case)) {
|
|
||||||
// When a case is closed, write the current metrics to the database
|
|
||||||
jobProcessingExecutor.submit(new ServicesHealthMonitor.DatabaseWriteTask());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,17 +19,25 @@
|
|||||||
package org.sleuthkit.autopsy.healthmonitor;
|
package org.sleuthkit.autopsy.healthmonitor;
|
||||||
|
|
||||||
import com.mchange.v2.cfg.DelayedLogItem;
|
import com.mchange.v2.cfg.DelayedLogItem;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.Font;
|
||||||
|
import javax.swing.Box;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JSeparator;
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
@ -48,51 +56,96 @@ public class HealthMonitorDashboard {
|
|||||||
|
|
||||||
private final static Logger logger = Logger.getLogger(HealthMonitorDashboard.class.getName());
|
private final static Logger logger = Logger.getLogger(HealthMonitorDashboard.class.getName());
|
||||||
|
|
||||||
//Map<String, List<ServicesHealthMonitor.DatabaseTimingResult>> timingData;
|
Map<String, List<EnterpriseHealthMonitor.DatabaseTimingResult>> timingData;
|
||||||
|
|
||||||
private JPanel timingMetricPanel = null;
|
private JPanel timingMetricPanel = null;
|
||||||
private JPanel timingButtonPanel = null;
|
private JPanel timingButtonPanel = null;
|
||||||
private JComboBox dateComboBox = null;
|
private JComboBox dateComboBox = null;
|
||||||
|
private JComboBox hostComboBox = null;
|
||||||
|
private JCheckBox hostCheckBox = null;
|
||||||
|
|
||||||
HealthMonitorDashboard() {
|
HealthMonitorDashboard() {
|
||||||
|
timingData = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void display() throws HealthMonitorException {
|
void display() throws HealthMonitorException {
|
||||||
|
|
||||||
// Initialize and populate the timing metric panel
|
// Get a copy of the timing data from the database
|
||||||
populateTimingMetricPanel();
|
timingData = EnterpriseHealthMonitor.getInstance().getTimingMetricsFromDatabase();
|
||||||
|
|
||||||
|
// Set up the buttons
|
||||||
|
setupTimingButtonPanel();
|
||||||
addActionListeners();
|
addActionListeners();
|
||||||
|
|
||||||
System.out.println("Creating dialog");
|
// Initialize and populate the timing metric panel
|
||||||
|
populateTimingMetricPanel();
|
||||||
|
|
||||||
JScrollPane scrollPane = new JScrollPane(timingMetricPanel);
|
JScrollPane scrollPane = new JScrollPane(timingMetricPanel);
|
||||||
JDialog dialog = new JDialog();
|
JDialog dialog = new JDialog();
|
||||||
dialog.setPreferredSize(new Dimension(1500, 800));
|
//dialog.setPreferredSize(new Dimension(1500, 800));
|
||||||
dialog.setTitle("Services Health Monitor");
|
dialog.setTitle("Enterprise Health Monitor");
|
||||||
//dialog.add(graphPanel);
|
//dialog.add(graphPanel);
|
||||||
dialog.add(scrollPane);
|
dialog.add(scrollPane);
|
||||||
dialog.pack();
|
dialog.pack();
|
||||||
dialog.setVisible(true);
|
dialog.setVisible(true);
|
||||||
System.out.println("Done displaying dialog");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the panel holding the timing metric controls,
|
* Initialize the panel holding the timing metric controls and
|
||||||
* if it has not already been initialized.
|
* update components
|
||||||
*/
|
*/
|
||||||
private void initializeTimingButtonPanel() throws HealthMonitorException {
|
private void setupTimingButtonPanel() throws HealthMonitorException {
|
||||||
if(timingButtonPanel == null) {
|
if(timingButtonPanel == null) {
|
||||||
|
timingButtonPanel = new JPanel();
|
||||||
|
timingButtonPanel.setBorder(BorderFactory.createEtchedBorder());
|
||||||
|
//timingButtonPanel.setPreferredSize(new Dimension(1500, 100));
|
||||||
|
} else {
|
||||||
|
timingButtonPanel.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The contents of the date combo box will never change, so we only need to
|
||||||
|
// do this once
|
||||||
|
if(dateComboBox == null) {
|
||||||
// Create the combo box for selecting how much data to display
|
// Create the combo box for selecting how much data to display
|
||||||
String[] dateOptionStrings = Arrays.stream(DateRange.values()).map(e -> e.getLabel()).toArray(String[]::new);
|
String[] dateOptionStrings = Arrays.stream(DateRange.values()).map(e -> e.getLabel()).toArray(String[]::new);
|
||||||
dateComboBox = new JComboBox(dateOptionStrings);
|
dateComboBox = new JComboBox(dateOptionStrings);
|
||||||
dateComboBox.setSelectedItem(DateRange.TWO_WEEKS.getLabel());
|
dateComboBox.setSelectedItem(DateRange.TWO_WEEKS.getLabel());
|
||||||
|
|
||||||
// Add the date range button and label to the panel
|
|
||||||
timingButtonPanel = new JPanel();
|
|
||||||
timingButtonPanel.setBorder(BorderFactory.createEtchedBorder());
|
|
||||||
timingButtonPanel.add(new JLabel("Max days to display"));
|
|
||||||
timingButtonPanel.add(dateComboBox);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create an array of host names
|
||||||
|
Set<String> hostNameSet = new HashSet<>();
|
||||||
|
for(String metricType:timingData.keySet()) {
|
||||||
|
for(EnterpriseHealthMonitor.DatabaseTimingResult result: timingData.get(metricType)) {
|
||||||
|
hostNameSet.add(result.getHostName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the host names into the combo box
|
||||||
|
hostComboBox = new JComboBox(hostNameSet.toArray(new String[hostNameSet.size()]));
|
||||||
|
|
||||||
|
// Create the checkbox (if needed)
|
||||||
|
if(hostCheckBox == null) {
|
||||||
|
hostCheckBox = new JCheckBox("Filter by host");
|
||||||
|
hostCheckBox.setSelected(false);
|
||||||
|
hostComboBox.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the panel
|
||||||
|
timingButtonPanel = new JPanel();
|
||||||
|
timingButtonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
|
//timingButtonPanel.setBorder(BorderFactory.createEtchedBorder());
|
||||||
|
|
||||||
|
// Add the date range combo box and label to the panel
|
||||||
|
timingButtonPanel.add(new JLabel("Max days to display"));
|
||||||
|
timingButtonPanel.add(dateComboBox);
|
||||||
|
|
||||||
|
// Put some space between the elements
|
||||||
|
timingButtonPanel.add(Box.createHorizontalStrut(100));
|
||||||
|
|
||||||
|
// Add the host combo box and checkbox to the panel
|
||||||
|
timingButtonPanel.add(hostCheckBox);
|
||||||
|
//timingButtonPanel.add(new JLabel("Host to display"));
|
||||||
|
timingButtonPanel.add(hostComboBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,18 +153,52 @@ public class HealthMonitorDashboard {
|
|||||||
* Call this after all components are initialized.
|
* Call this after all components are initialized.
|
||||||
*/
|
*/
|
||||||
private void addActionListeners() {
|
private void addActionListeners() {
|
||||||
|
|
||||||
// Set up a listener on the combo box that will update the timing
|
// Set up a listener on the combo box that will update the timing
|
||||||
// metric graphs
|
// metric graphs
|
||||||
dateComboBox.addActionListener(new ActionListener() {
|
if((dateComboBox != null) && (dateComboBox.getActionListeners().length == 0)) {
|
||||||
@Override
|
dateComboBox.addActionListener(new ActionListener() {
|
||||||
public void actionPerformed(ActionEvent arg0) {
|
@Override
|
||||||
try {
|
public void actionPerformed(ActionEvent arg0) {
|
||||||
populateTimingMetricPanel();
|
try {
|
||||||
} catch (HealthMonitorException ex) {
|
populateTimingMetricPanel();
|
||||||
logger.log(Level.SEVERE, "Error populating timing metric panel", ex);
|
} catch (HealthMonitorException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error populating timing metric panel", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Set up a listener on the host name combo box
|
||||||
|
if((hostComboBox != null) && (hostComboBox.getActionListeners().length == 0)) {
|
||||||
|
hostComboBox.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent arg0) {
|
||||||
|
try {
|
||||||
|
if((hostCheckBox != null) && hostCheckBox.isSelected()) {
|
||||||
|
populateTimingMetricPanel();
|
||||||
|
}
|
||||||
|
} catch (HealthMonitorException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error populating timing metric panel", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a listener on the host name check box
|
||||||
|
if((hostCheckBox != null) && (hostCheckBox.getActionListeners().length == 0)) {
|
||||||
|
hostCheckBox.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent arg0) {
|
||||||
|
try {
|
||||||
|
hostComboBox.setEnabled(hostCheckBox.isSelected()); // Why isn't this working?
|
||||||
|
populateTimingMetricPanel();
|
||||||
|
} catch (HealthMonitorException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error populating timing metric panel", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,38 +218,52 @@ public class HealthMonitorDashboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void populateTimingMetricPanel() throws HealthMonitorException {
|
private void populateTimingMetricPanel() throws HealthMonitorException {
|
||||||
initializeTimingButtonPanel();
|
|
||||||
initializeTimingMetricPanel();
|
initializeTimingMetricPanel();
|
||||||
|
|
||||||
// Get a fresh copy of the timing data from the database
|
// Add title
|
||||||
Map<String, List<ServicesHealthMonitor.DatabaseTimingResult>> timingData = ServicesHealthMonitor.getInstance().getTimingMetricsFromDatabase();
|
JLabel timingMetricTitle = new JLabel("Timing Metrics");
|
||||||
|
timingMetricTitle.setFont(new Font("Serif", Font.BOLD, 20));
|
||||||
|
timingMetricPanel.add(timingMetricTitle);
|
||||||
|
|
||||||
// Add the button controls
|
// Add the button controls
|
||||||
timingMetricPanel.add(timingButtonPanel);
|
timingMetricPanel.add(timingButtonPanel);
|
||||||
|
timingMetricPanel.add(new JSeparator());
|
||||||
|
|
||||||
for(String name:timingData.keySet()) {
|
for(String name:timingData.keySet()) {
|
||||||
// Add the metric name
|
// Add the metric name
|
||||||
JLabel label = new JLabel(name);
|
JLabel metricNameLabel = new JLabel(name);
|
||||||
timingMetricPanel.add(label);
|
metricNameLabel.setFont(new Font("Serif", Font.BOLD, 12));
|
||||||
|
timingMetricPanel.add(metricNameLabel);
|
||||||
|
|
||||||
// If necessary, trim down the list of results to fit the selected time range
|
// If necessary, trim down the list of results to fit the selected time range
|
||||||
List<ServicesHealthMonitor.DatabaseTimingResult> timingDataForDisplay;
|
List<EnterpriseHealthMonitor.DatabaseTimingResult> intermediateTimingDataForDisplay;
|
||||||
if(dateComboBox.getSelectedItem() != null) {
|
if(dateComboBox.getSelectedItem() != null) {
|
||||||
DateRange selectedDateRange = DateRange.fromLabel(dateComboBox.getSelectedItem().toString());
|
DateRange selectedDateRange = DateRange.fromLabel(dateComboBox.getSelectedItem().toString());
|
||||||
if(selectedDateRange != DateRange.ALL) {
|
if(selectedDateRange != DateRange.ALL) {
|
||||||
long threshold = System.currentTimeMillis() - selectedDateRange.getTimestampRange();
|
long threshold = System.currentTimeMillis() - selectedDateRange.getTimestampRange();
|
||||||
timingDataForDisplay = timingData.get(name).stream()
|
intermediateTimingDataForDisplay = timingData.get(name).stream()
|
||||||
.filter(t -> t.getTimestamp() > threshold)
|
.filter(t -> t.getTimestamp() > threshold)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
} else {
|
} else {
|
||||||
timingDataForDisplay = timingData.get(name);
|
intermediateTimingDataForDisplay = timingData.get(name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
timingDataForDisplay = timingData.get(name);
|
intermediateTimingDataForDisplay = timingData.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If necessary, trim down the resulting list to only one host name
|
||||||
|
List<EnterpriseHealthMonitor.DatabaseTimingResult> timingDataForDisplay;
|
||||||
|
if(hostCheckBox.isSelected() && (hostComboBox.getSelectedItem() != null)) {
|
||||||
|
timingDataForDisplay = intermediateTimingDataForDisplay.stream()
|
||||||
|
.filter(t -> t.getHostName().equals(hostComboBox.getSelectedItem().toString()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} else {
|
||||||
|
timingDataForDisplay = intermediateTimingDataForDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
TimingMetricGraphPanel singleTimingGraphPanel = new TimingMetricGraphPanel(timingDataForDisplay, TimingMetricGraphPanel.TimingMetricType.AVERAGE, true);
|
TimingMetricGraphPanel singleTimingGraphPanel = new TimingMetricGraphPanel(timingDataForDisplay, TimingMetricGraphPanel.TimingMetricType.AVERAGE, true);
|
||||||
singleTimingGraphPanel.setPreferredSize(new Dimension(800,200));
|
singleTimingGraphPanel.setPreferredSize(new Dimension(900,250));
|
||||||
timingMetricPanel.add(singleTimingGraphPanel);
|
timingMetricPanel.add(singleTimingGraphPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
|||||||
public class Installer extends ModuleInstall {
|
public class Installer extends ModuleInstall {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Installer.class.getName());
|
private static final Logger logger = Logger.getLogger(Installer.class.getName());
|
||||||
private final HealthMonitorCaseEventListener pcl = new HealthMonitorCaseEventListener();
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static Installer instance;
|
private static Installer instance;
|
||||||
@ -45,10 +44,8 @@ public class Installer extends ModuleInstall {
|
|||||||
@Override
|
@Override
|
||||||
public void restored() {
|
public void restored() {
|
||||||
|
|
||||||
Case.addPropertyChangeListener(pcl);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ServicesHealthMonitor.startUp();
|
EnterpriseHealthMonitor.startUpIfEnabled();
|
||||||
} catch (HealthMonitorException ex) {
|
} catch (HealthMonitorException ex) {
|
||||||
logger.log(Level.SEVERE, "Error starting health services monitor", ex);
|
logger.log(Level.SEVERE, "Error starting health services monitor", ex);
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
<Component id="graphButton" min="-2" max="-2" attributes="0"/>
|
<Component id="graphButton" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace min="-2" pref="142" max="-2" attributes="0"/>
|
<EmptySpace min="-2" pref="142" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<Group type="102" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||||
<Component id="jLabel4" max="32767" attributes="0"/>
|
<Component id="jLabel4" max="32767" attributes="0"/>
|
||||||
<Component id="nDaysTextField" max="32767" attributes="0"/>
|
<Component id="nDaysTextField" max="32767" attributes="0"/>
|
||||||
@ -84,7 +84,10 @@
|
|||||||
<Component id="nNodesTextField" max="32767" attributes="0"/>
|
<Component id="nNodesTextField" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||||
<Component id="jButton2" min="-2" max="-2" attributes="0"/>
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="jButton3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="jButton2" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
@ -132,7 +135,9 @@
|
|||||||
<Component id="nNodesTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="nNodesTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace pref="66" max="32767" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="jButton3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace pref="37" max="32767" attributes="0"/>
|
||||||
<Component id="textExistButton" min="-2" max="-2" attributes="0"/>
|
<Component id="textExistButton" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
@ -334,5 +339,15 @@
|
|||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newGraphButtonActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newGraphButtonActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="jButton3">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/healthmonitor/Bundle.properties" key="TestPanel.jButton3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton3ActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -18,7 +18,7 @@ import javax.swing.JScrollPane;
|
|||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import org.sleuthkit.autopsy.healthmonitor.ServicesHealthMonitor.DatabaseTimingResult; // TEMP TEMP
|
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor.DatabaseTimingResult; // TEMP TEMP
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is for testing the Health Monitor code
|
* This is for testing the Health Monitor code
|
||||||
@ -62,6 +62,7 @@ public class TestPanel extends javax.swing.JDialog {
|
|||||||
nNodesTextField = new javax.swing.JTextField();
|
nNodesTextField = new javax.swing.JTextField();
|
||||||
jButton2 = new javax.swing.JButton();
|
jButton2 = new javax.swing.JButton();
|
||||||
newGraphButton = new javax.swing.JButton();
|
newGraphButton = new javax.swing.JButton();
|
||||||
|
jButton3 = new javax.swing.JButton();
|
||||||
|
|
||||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||||
|
|
||||||
@ -175,6 +176,13 @@ public class TestPanel extends javax.swing.JDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(jButton3, org.openide.util.NbBundle.getMessage(TestPanel.class, "TestPanel.jButton3.text")); // NOI18N
|
||||||
|
jButton3.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
jButton3ActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||||
getContentPane().setLayout(layout);
|
getContentPane().setLayout(layout);
|
||||||
layout.setHorizontalGroup(
|
layout.setHorizontalGroup(
|
||||||
@ -227,7 +235,9 @@ public class TestPanel extends javax.swing.JDialog {
|
|||||||
.addComponent(jLabel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
.addComponent(jLabel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
.addComponent(nNodesTextField))
|
.addComponent(nNodesTextField))
|
||||||
.addGap(18, 18, 18)
|
.addGap(18, 18, 18)
|
||||||
.addComponent(jButton2)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(jButton3)
|
||||||
|
.addComponent(jButton2))
|
||||||
.addGap(0, 0, Short.MAX_VALUE))))
|
.addGap(0, 0, Short.MAX_VALUE))))
|
||||||
);
|
);
|
||||||
layout.setVerticalGroup(
|
layout.setVerticalGroup(
|
||||||
@ -264,7 +274,9 @@ public class TestPanel extends javax.swing.JDialog {
|
|||||||
.addComponent(nDaysTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(nDaysTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(nNodesTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(nNodesTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(jButton2))
|
.addComponent(jButton2))
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 66, Short.MAX_VALUE)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(jButton3)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 37, Short.MAX_VALUE)
|
||||||
.addComponent(textExistButton)
|
.addComponent(textExistButton)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
@ -300,18 +312,18 @@ public class TestPanel extends javax.swing.JDialog {
|
|||||||
if(nameTextField.getText().isEmpty() || durationTextField.getText().isEmpty()) {
|
if(nameTextField.getText().isEmpty() || durationTextField.getText().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TimingMetric m = ServicesHealthMonitor.getTimingMetric(nameTextField.getText());
|
TimingMetric m = EnterpriseHealthMonitor.getTimingMetric(nameTextField.getText());
|
||||||
try {
|
try {
|
||||||
Thread.sleep(Long.parseLong(durationTextField.getText()));
|
Thread.sleep(Long.parseLong(durationTextField.getText()));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
ServicesHealthMonitor.submitTimingMetric(m);
|
EnterpriseHealthMonitor.submitTimingMetric(m);
|
||||||
}//GEN-LAST:event_submitMetricButtonActionPerformed
|
}//GEN-LAST:event_submitMetricButtonActionPerformed
|
||||||
|
|
||||||
private void enabledCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enabledCheckBoxActionPerformed
|
private void enabledCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enabledCheckBoxActionPerformed
|
||||||
try {
|
try {
|
||||||
ServicesHealthMonitor.setEnabled(enabledCheckBox.isSelected());
|
EnterpriseHealthMonitor.setEnabled(enabledCheckBox.isSelected());
|
||||||
} catch (HealthMonitorException ex) {
|
} catch (HealthMonitorException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -357,7 +369,7 @@ public class TestPanel extends javax.swing.JDialog {
|
|||||||
// TEMP TEMP
|
// TEMP TEMP
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Map<String, List<DatabaseTimingResult>> timingData = ServicesHealthMonitor.getInstance().getTimingMetricsFromDatabase();
|
Map<String, List<DatabaseTimingResult>> timingData = EnterpriseHealthMonitor.getInstance().getTimingMetricsFromDatabase();
|
||||||
|
|
||||||
String[] dateOptionStrings = {"All", "Two weeks", "One week"};
|
String[] dateOptionStrings = {"All", "Two weeks", "One week"};
|
||||||
JComboBox dateComboBox = new JComboBox(dateOptionStrings);
|
JComboBox dateComboBox = new JComboBox(dateOptionStrings);
|
||||||
@ -422,7 +434,7 @@ public class TestPanel extends javax.swing.JDialog {
|
|||||||
try {
|
try {
|
||||||
int nDays = Integer.valueOf(nDaysTextField.getText());
|
int nDays = Integer.valueOf(nDaysTextField.getText());
|
||||||
int nNodes = Integer.valueOf(nNodesTextField.getText());
|
int nNodes = Integer.valueOf(nNodesTextField.getText());
|
||||||
ServicesHealthMonitor.getInstance().populateDatabase(nDays, nNodes); // TEMP TEMP
|
EnterpriseHealthMonitor.getInstance().populateDatabase(nDays, nNodes, false); // TEMP TEMP
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -438,6 +450,21 @@ public class TestPanel extends javax.swing.JDialog {
|
|||||||
}
|
}
|
||||||
}//GEN-LAST:event_newGraphButtonActionPerformed
|
}//GEN-LAST:event_newGraphButtonActionPerformed
|
||||||
|
|
||||||
|
private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed
|
||||||
|
if(nDaysTextField.getText().isEmpty() || nNodesTextField.getText().isEmpty()) {
|
||||||
|
System.out.println("Missing fields");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int nDays = Integer.valueOf(nDaysTextField.getText());
|
||||||
|
int nNodes = Integer.valueOf(nNodesTextField.getText());
|
||||||
|
EnterpriseHealthMonitor.getInstance().populateDatabase(nDays, nNodes, true); // TEMP TEMP
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}//GEN-LAST:event_jButton3ActionPerformed
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JButton closeButton;
|
private javax.swing.JButton closeButton;
|
||||||
private javax.swing.JButton deleteButton;
|
private javax.swing.JButton deleteButton;
|
||||||
@ -446,6 +473,7 @@ public class TestPanel extends javax.swing.JDialog {
|
|||||||
private javax.swing.JButton graphButton;
|
private javax.swing.JButton graphButton;
|
||||||
private javax.swing.JButton jButton1;
|
private javax.swing.JButton jButton1;
|
||||||
private javax.swing.JButton jButton2;
|
private javax.swing.JButton jButton2;
|
||||||
|
private javax.swing.JButton jButton3;
|
||||||
private javax.swing.JLabel jLabel1;
|
private javax.swing.JLabel jLabel1;
|
||||||
private javax.swing.JLabel jLabel2;
|
private javax.swing.JLabel jLabel2;
|
||||||
private javax.swing.JLabel jLabel3;
|
private javax.swing.JLabel jLabel3;
|
||||||
|
@ -18,10 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.healthmonitor;
|
package org.sleuthkit.autopsy.healthmonitor;
|
||||||
|
|
||||||
import com.mchange.v2.cfg.DelayedLogItem;
|
|
||||||
import java.awt.BasicStroke;
|
import java.awt.BasicStroke;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
@ -37,24 +35,23 @@ import java.util.GregorianCalendar;
|
|||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.healthmonitor.ServicesHealthMonitor.DatabaseTimingResult;
|
import java.util.TimeZone;
|
||||||
|
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor.DatabaseTimingResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Creates a graph of the given timing metric data
|
||||||
*/
|
*/
|
||||||
class TimingMetricGraphPanel extends JPanel {
|
class TimingMetricGraphPanel extends JPanel {
|
||||||
|
|
||||||
private final static Logger logger = Logger.getLogger(TimingMetricGraphPanel.class.getName());
|
private final static Logger logger = Logger.getLogger(TimingMetricGraphPanel.class.getName());
|
||||||
|
|
||||||
private int width = 800;
|
|
||||||
private int heigth = 400;
|
|
||||||
private int padding = 25;
|
private int padding = 25;
|
||||||
private int labelPadding = 25;
|
private int labelPadding = 25;
|
||||||
private Color lineColor = new Color(0x12, 0x20, 0xdb, 180);
|
private Color lineColor = new Color(0x12, 0x20, 0xdb, 180);
|
||||||
private Color pointColor = new Color(100, 100, 100, 180);
|
|
||||||
private Color gridColor = new Color(200, 200, 200, 200);
|
private Color gridColor = new Color(200, 200, 200, 200);
|
||||||
private Color trendLineColor = new Color(150, 10, 10, 200);
|
private Color trendLineColor = new Color(150, 10, 10, 200);
|
||||||
private static final Stroke GRAPH_STROKE = new BasicStroke(2f);
|
private static final Stroke GRAPH_STROKE = new BasicStroke(2f);
|
||||||
|
private static final Stroke NARROW_STROKE = new BasicStroke(1f);
|
||||||
private int pointWidth = 4;
|
private int pointWidth = 4;
|
||||||
private int numberYDivisions = 10;
|
private int numberYDivisions = 10;
|
||||||
private List<DatabaseTimingResult> timingResults;
|
private List<DatabaseTimingResult> timingResults;
|
||||||
@ -76,21 +73,10 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void loadNewData(List<DatabaseTimingResult> timingResults, TimingMetricType timingMetricType, boolean doLineGraph) {
|
/**
|
||||||
this.timingResults = timingResults;
|
* Get the highest metric time for the given type
|
||||||
this.timingMetricType = timingMetricType;
|
* @return the highest metric time
|
||||||
this.doLineGraph = doLineGraph;
|
*/
|
||||||
try {
|
|
||||||
trendLine = new TrendLine(timingResults, timingMetricType);
|
|
||||||
} catch (HealthMonitorException ex) {
|
|
||||||
// Log it, set trendLine to null and continue on
|
|
||||||
logger.log(Level.WARNING, "Can not generate a trend line on empty data set");
|
|
||||||
trendLine = null;
|
|
||||||
}
|
|
||||||
this.revalidate();
|
|
||||||
this.repaint();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private double getMaxMetricTime() {
|
private double getMaxMetricTime() {
|
||||||
// Find the highest of the values being graphed
|
// Find the highest of the values being graphed
|
||||||
double maxScore = Double.MIN_VALUE;
|
double maxScore = Double.MIN_VALUE;
|
||||||
@ -112,6 +98,10 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
return maxScore;
|
return maxScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the lowest metric time for the given type
|
||||||
|
* @return the lowest metric time
|
||||||
|
*/
|
||||||
private double getMinMetricTime() {
|
private double getMinMetricTime() {
|
||||||
// Find the lowest of the values being graphed
|
// Find the lowest of the values being graphed
|
||||||
double minScore = Double.MAX_VALUE;
|
double minScore = Double.MAX_VALUE;
|
||||||
@ -133,6 +123,10 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
return minScore;
|
return minScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the largest timestamp in the data collection
|
||||||
|
* @return the largest timestamp
|
||||||
|
*/
|
||||||
private long getMaxTimestamp() {
|
private long getMaxTimestamp() {
|
||||||
long maxTimestamp = Long.MIN_VALUE;
|
long maxTimestamp = Long.MIN_VALUE;
|
||||||
for (DatabaseTimingResult score : timingResults) {
|
for (DatabaseTimingResult score : timingResults) {
|
||||||
@ -141,6 +135,10 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
return maxTimestamp;
|
return maxTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the smallest timestamp in the data collection
|
||||||
|
* @return the minimum timestamp
|
||||||
|
*/
|
||||||
private long getMinTimestamp() {
|
private long getMinTimestamp() {
|
||||||
long minTimestamp = Long.MAX_VALUE;
|
long minTimestamp = Long.MAX_VALUE;
|
||||||
for (DatabaseTimingResult score : timingResults) {
|
for (DatabaseTimingResult score : timingResults) {
|
||||||
@ -150,7 +148,14 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Setup of the graphics panel:
|
||||||
* Origin (0,0) is at the top left corner
|
* Origin (0,0) is at the top left corner
|
||||||
|
*
|
||||||
|
* Horizontally (from the left): (padding)(label padding)(the graph)(padding)
|
||||||
|
* For plotting data on the x-axis, we scale it to the size of the graph and then add the padding and label padding
|
||||||
|
*
|
||||||
|
* Vertically (from the top): (padding)(the graph)(label padding)(padding)
|
||||||
|
* For plotting data on the y-axis, we subtract from the max value in the graph and then scale to the size of the graph
|
||||||
* @param g
|
* @param g
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ -159,43 +164,69 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
Graphics2D g2 = (Graphics2D) g;
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
// Get the max and min timestamps and convert to days to create the x-axis
|
// Get the max and min timestamps to create the x-axis.
|
||||||
|
// We add a small buffer to each side so the data won't overwrite the axes.
|
||||||
long originalMaxTimestamp = getMaxTimestamp();
|
long originalMaxTimestamp = getMaxTimestamp();
|
||||||
double maxTimestamp = (double) originalMaxTimestamp + 1000 * 60 *60 * 4; // Four hour buffer
|
double maxValueOnXAxis = (double) originalMaxTimestamp + 1000 * 60 *60 * 2; // Two hour buffer
|
||||||
double minTimestamp = getMinTimestamp() - 1000 * 60 * 60 * 4; // Four hour buffer
|
double minValueOnXAxis = getMinTimestamp() - 1000 * 60 * 60 * 2; // Two hour buffer
|
||||||
System.out.println("originalMax: " + originalMaxTimestamp + " new max: " + maxTimestamp + " new min: " + minTimestamp);
|
|
||||||
|
|
||||||
// Get the max and min times to create the y-axis
|
// Get the max and min times to create the y-axis
|
||||||
double maxMetricTime = getMaxMetricTime();
|
// We add a small buffer to each side so the data won't overwrite the axes.
|
||||||
double minMetricTime = getMinMetricTime();
|
double maxValueOnYAxis = getMaxMetricTime();
|
||||||
minMetricTime = Math.max(0, minMetricTime - (maxMetricTime * 0.1));
|
double minValueOnYAxis = getMinMetricTime();
|
||||||
maxMetricTime = maxMetricTime * 1.1;
|
minValueOnYAxis = Math.max(0, minValueOnYAxis - (maxValueOnYAxis * 0.1));
|
||||||
|
maxValueOnYAxis = maxValueOnYAxis * 1.1;
|
||||||
|
|
||||||
double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (maxTimestamp - minTimestamp);
|
// The graph itself has the following corners:
|
||||||
double yScale = ((double) getHeight() - (2 * padding) - labelPadding) / (maxMetricTime - minMetricTime);
|
// (padding + label padding, padding) - top left
|
||||||
|
// (padding + label padding, getHeight() - label padding - padding x 2) - bottom left
|
||||||
|
// (getWidth() - padding, getHeight() - label padding - padding x 2) - top right ??
|
||||||
|
// (padding + label padding, getHeight() - label padding - padding x 2) - bottom right
|
||||||
|
int leftGraphPadding = padding + labelPadding;
|
||||||
|
int rightGraphPadding = padding;
|
||||||
|
int topGraphPadding = padding;
|
||||||
|
int bottomGraphPadding = padding + labelPadding;
|
||||||
|
|
||||||
|
// Calculate the scale for each axis.
|
||||||
|
// The size of the graph area is the width/height of the panel minus any padding.
|
||||||
|
// The scale is calculated based on this size of the graph compared to the data range.
|
||||||
|
// For example:
|
||||||
|
// getWidth() = 575 => graph width = 500
|
||||||
|
// If our max x value to plot is 10000 and our min is 0, then the xScale would be 0.05 - i.e.,
|
||||||
|
// our original x values will be multipled by 0.05 to translate them to an x-coordinate in the
|
||||||
|
// graph (plus the padding)
|
||||||
|
int graphWidth = getWidth() - leftGraphPadding - rightGraphPadding;
|
||||||
|
int graphHeight = getHeight() - topGraphPadding - bottomGraphPadding;
|
||||||
|
double xScale = ((double) graphWidth) / (maxValueOnXAxis - minValueOnXAxis);
|
||||||
|
double yScale = ((double) graphHeight) / (maxValueOnYAxis - minValueOnYAxis);
|
||||||
|
|
||||||
System.out.println("xScale: " + xScale + ", yScale: " + yScale);
|
|
||||||
|
|
||||||
// draw white background
|
// draw white background
|
||||||
g2.setColor(Color.WHITE);
|
g2.setColor(Color.WHITE);
|
||||||
g2.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding, getHeight() - 2 * padding - labelPadding);
|
g2.fillRect(leftGraphPadding, topGraphPadding, graphWidth, graphHeight);
|
||||||
g2.setColor(Color.BLACK);
|
|
||||||
|
|
||||||
// create hatch marks and grid lines for y axis.
|
// create hatch marks and grid lines for y axis.
|
||||||
for (int i = 0; i < numberYDivisions + 1; i++) {
|
for (int i = 0; i < numberYDivisions + 1; i++) {
|
||||||
int x0 = padding + labelPadding;
|
int x0 = leftGraphPadding;
|
||||||
int x1 = pointWidth + padding + labelPadding;
|
int x1 = pointWidth + leftGraphPadding;
|
||||||
int y0 = getHeight() - ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding);
|
int y0 = getHeight() - ((i * graphHeight) / numberYDivisions + bottomGraphPadding);
|
||||||
int y1 = y0;
|
int y1 = y0;
|
||||||
|
|
||||||
if (timingResults.size() > 0) {
|
if (timingResults.size() > 0) {
|
||||||
|
// Draw the grid line
|
||||||
g2.setColor(gridColor);
|
g2.setColor(gridColor);
|
||||||
g2.drawLine(padding + labelPadding + 1 + pointWidth, y0, getWidth() - padding, y1);
|
g2.drawLine(leftGraphPadding + 1 + pointWidth, y0, getWidth() - rightGraphPadding, y1);
|
||||||
|
|
||||||
|
// Create the label
|
||||||
g2.setColor(Color.BLACK);
|
g2.setColor(Color.BLACK);
|
||||||
String yLabel = ((int) ((getMinMetricTime() + (maxMetricTime - minMetricTime) * ((i * 1.0) / numberYDivisions)) * 100)) / 100.0 + "";
|
double yValue = minValueOnYAxis + ((maxValueOnYAxis - minValueOnYAxis) * ((i * 1.0) / numberYDivisions));
|
||||||
FontMetrics metrics = g2.getFontMetrics();
|
String yLabel = ((int) (yValue * 100)) / 100.0 + "";
|
||||||
int labelWidth = metrics.stringWidth(yLabel);
|
FontMetrics fontMetrics = g2.getFontMetrics();
|
||||||
g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3);
|
int labelWidth = fontMetrics.stringWidth(yLabel);
|
||||||
|
g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (fontMetrics.getHeight() / 2) - 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw the small hatch mark
|
||||||
|
g2.setColor(Color.BLACK);
|
||||||
g2.drawLine(x0, y0, x1, y1);
|
g2.drawLine(x0, y0, x1, y1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,11 +238,10 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
maxDate.set(Calendar.SECOND, 0);
|
maxDate.set(Calendar.SECOND, 0);
|
||||||
maxDate.set(Calendar.MILLISECOND, 0);
|
maxDate.set(Calendar.MILLISECOND, 0);
|
||||||
long maxMidnightInMillis = maxDate.getTimeInMillis();
|
long maxMidnightInMillis = maxDate.getTimeInMillis();
|
||||||
System.out.println("Last timestamp: " + originalMaxTimestamp + ", last midnight: " + maxMidnightInMillis);
|
|
||||||
|
|
||||||
// We don't want to display more than 20 grid lines
|
// We don't want to display more than 20 grid lines. If we have more
|
||||||
long totalDays = (maxMidnightInMillis - getMinTimestamp()) / MILLISECONDS_PER_DAY;
|
// data then that, put multiple days within one division
|
||||||
System.out.println(" Total days: " + totalDays);
|
long totalDays = (maxMidnightInMillis - (long)minValueOnXAxis) / MILLISECONDS_PER_DAY;
|
||||||
long daysPerDivision;
|
long daysPerDivision;
|
||||||
if(totalDays <= 20) {
|
if(totalDays <= 20) {
|
||||||
daysPerDivision = 1;
|
daysPerDivision = 1;
|
||||||
@ -223,16 +253,19 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw the vertical grid lines and labels
|
// Draw the vertical grid lines and labels
|
||||||
for (long currentDivision = maxMidnightInMillis; currentDivision >= minTimestamp; currentDivision -= MILLISECONDS_PER_DAY * daysPerDivision) {
|
// The vertical grid lines will be at midnight, and display the date underneath them
|
||||||
|
// At present we use GMT because of some complications with daylight savings time.
|
||||||
|
for (long currentDivision = maxMidnightInMillis; currentDivision >= minValueOnXAxis; currentDivision -= MILLISECONDS_PER_DAY * daysPerDivision) {
|
||||||
|
|
||||||
int x0 = (int) ((double)(currentDivision - minTimestamp) * xScale + padding + labelPadding);
|
//long currentDivision =
|
||||||
|
int x0 = (int) ((double)(currentDivision - minValueOnXAxis) * xScale + leftGraphPadding);
|
||||||
int x1 = x0;
|
int x1 = x0;
|
||||||
int y0 = getHeight() - padding - labelPadding;
|
int y0 = getHeight() - bottomGraphPadding;
|
||||||
int y1 = y0 - pointWidth;
|
int y1 = y0 - pointWidth;
|
||||||
|
|
||||||
// Draw the light grey grid line
|
// Draw the light grey grid line
|
||||||
g2.setColor(gridColor);
|
g2.setColor(gridColor);
|
||||||
g2.drawLine(x0, getHeight() - padding - labelPadding - 1 - pointWidth, x1, padding);
|
g2.drawLine(x0, getHeight() - bottomGraphPadding - 1 - pointWidth, x1, topGraphPadding);
|
||||||
|
|
||||||
// Draw the hatch mark
|
// Draw the hatch mark
|
||||||
g2.setColor(Color.BLACK);
|
g2.setColor(Color.BLACK);
|
||||||
@ -240,21 +273,23 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
|
|
||||||
// Draw the label
|
// Draw the label
|
||||||
Calendar thisDate = new GregorianCalendar();
|
Calendar thisDate = new GregorianCalendar();
|
||||||
|
thisDate.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||||
thisDate.setTimeInMillis(currentDivision);
|
thisDate.setTimeInMillis(currentDivision);
|
||||||
int month = thisDate.get(Calendar.MONTH) + 1;
|
int month = thisDate.get(Calendar.MONTH) + 1;
|
||||||
int day = thisDate.get(Calendar.DAY_OF_MONTH);
|
int day = thisDate.get(Calendar.DAY_OF_MONTH);
|
||||||
|
|
||||||
String xLabel = month + "/" + day;
|
String xLabel = month + "/" + day;
|
||||||
FontMetrics metrics = g2.getFontMetrics();
|
FontMetrics metrics = g2.getFontMetrics();
|
||||||
int labelWidth = metrics.stringWidth(xLabel);
|
int labelWidth = metrics.stringWidth(xLabel);
|
||||||
g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);
|
g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create x and y axes
|
// Create x and y axes
|
||||||
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, padding + labelPadding, padding);
|
g2.setColor(Color.BLACK);
|
||||||
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, getWidth() - padding, getHeight() - padding - labelPadding);
|
g2.drawLine(leftGraphPadding, getHeight() - bottomGraphPadding, leftGraphPadding, topGraphPadding);
|
||||||
|
g2.drawLine(leftGraphPadding, getHeight() - bottomGraphPadding, getWidth() - rightGraphPadding, getHeight() - bottomGraphPadding);
|
||||||
|
|
||||||
// Plot the timing points
|
// Create the points to plot
|
||||||
g2.setStroke(GRAPH_STROKE);
|
|
||||||
List<Point> graphPoints = new ArrayList<>();
|
List<Point> graphPoints = new ArrayList<>();
|
||||||
for (int i = 0; i < timingResults.size(); i++) {
|
for (int i = 0; i < timingResults.size(); i++) {
|
||||||
double metricTime;
|
double metricTime;
|
||||||
@ -272,9 +307,9 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int x1 = (int) ((double)(maxTimestamp - timingResults.get(i).getTimestamp()) * xScale + padding + labelPadding);
|
int x1 = (int) ((timingResults.get(i).getTimestamp() - minValueOnXAxis) * xScale + leftGraphPadding);
|
||||||
int yAve = (int) ((maxMetricTime - metricTime) * yScale + padding);
|
int y1 = (int) ((maxValueOnYAxis - metricTime) * yScale + topGraphPadding);
|
||||||
graphPoints.add(new Point(x1, yAve));
|
graphPoints.add(new Point(x1, y1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the points
|
// Sort the points
|
||||||
@ -289,14 +324,12 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
System.out.println("points: ");
|
|
||||||
for(Point p:graphPoints){
|
|
||||||
System.out.println(p.getX() + ", " + p.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Draw the selected type of graph. If there's only one data point,
|
||||||
|
// draw it.
|
||||||
|
g2.setStroke(NARROW_STROKE);
|
||||||
g2.setColor(lineColor);
|
g2.setColor(lineColor);
|
||||||
if(doLineGraph) {
|
if(doLineGraph && graphPoints.size() > 1) {
|
||||||
for (int i = 0; i < graphPoints.size() - 1; i++) {
|
for (int i = 0; i < graphPoints.size() - 1; i++) {
|
||||||
int x1 = graphPoints.get(i).x;
|
int x1 = graphPoints.get(i).x;
|
||||||
int y1 = graphPoints.get(i).y;
|
int y1 = graphPoints.get(i).y;
|
||||||
@ -315,20 +348,99 @@ class TimingMetricGraphPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw the trend line
|
// Draw the trend line
|
||||||
int x0 = (int) ((double)(maxTimestamp - minTimestamp) * xScale + padding + labelPadding);
|
// Don't draw anything if there's only one data point
|
||||||
int y0 = (int) ((double)(maxMetricTime - trendLine.getExpectedValueAt(minTimestamp)) * yScale + padding);
|
if(trendLine != null && (timingResults.size() > 1)) {
|
||||||
int x1 = (int) ((double)(0) * xScale + padding + labelPadding);
|
int x0 = (int) ((double)(0) * xScale + padding + labelPadding);
|
||||||
int y1 = (int) ((double)(maxMetricTime - trendLine.getExpectedValueAt(maxTimestamp)) * yScale + padding);
|
int y0 = (int) ((double)(maxValueOnYAxis - trendLine.getExpectedValueAt(minValueOnXAxis)) * yScale + padding);
|
||||||
g2.setColor(trendLineColor);
|
int x1 = (int) ((double)(maxValueOnXAxis - minValueOnXAxis) * xScale + padding + labelPadding);
|
||||||
g2.drawLine(x0, y0, x1, y1);
|
int y1 = (int) ((double)(maxValueOnYAxis - trendLine.getExpectedValueAt(maxValueOnXAxis)) * yScale + padding);
|
||||||
|
g2.setStroke(GRAPH_STROKE);
|
||||||
// TODO - temp testing where origin is
|
g2.setColor(trendLineColor);
|
||||||
g2.fillOval(0, 0, 20, 20);
|
g2.drawLine(x0, y0, x1, y1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The metric field we want to graph
|
||||||
|
*/
|
||||||
enum TimingMetricType {
|
enum TimingMetricType {
|
||||||
AVERAGE,
|
AVERAGE,
|
||||||
MAX,
|
MAX,
|
||||||
MIN;
|
MIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to generate a linear trend line from timing metric data.
|
||||||
|
*
|
||||||
|
* Formula for the linear trend line:
|
||||||
|
* (x,y) = (timestamp, metric time)
|
||||||
|
* n = total number of metrics
|
||||||
|
*
|
||||||
|
* slope = ( n * Σ(xy) - Σx * Σy ) / ( n * Σ(x^2) - (Σx)^2 )
|
||||||
|
*
|
||||||
|
* y intercept = ( Σy - (slope) * Σx ) / n
|
||||||
|
*/
|
||||||
|
class TrendLine {
|
||||||
|
|
||||||
|
double slope;
|
||||||
|
double yInt;
|
||||||
|
|
||||||
|
TrendLine(List<DatabaseTimingResult> timingResults, TimingMetricGraphPanel.TimingMetricType timingMetricType) throws HealthMonitorException {
|
||||||
|
|
||||||
|
if((timingResults == null) || timingResults.isEmpty()) {
|
||||||
|
throw new HealthMonitorException("Can not generate trend line for empty/null data set");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate intermediate values
|
||||||
|
int n = timingResults.size();
|
||||||
|
double sumX = 0;
|
||||||
|
double sumY = 0;
|
||||||
|
double sumXY = 0;
|
||||||
|
double sumXsquared = 0;
|
||||||
|
for(int i = 0;i < n;i++) {
|
||||||
|
double x = timingResults.get(i).getTimestamp();
|
||||||
|
double y;
|
||||||
|
switch (timingMetricType) {
|
||||||
|
case MAX:
|
||||||
|
y = timingResults.get(i).getMax();
|
||||||
|
break;
|
||||||
|
case MIN:
|
||||||
|
y = timingResults.get(i).getMin();
|
||||||
|
break;
|
||||||
|
case AVERAGE:
|
||||||
|
default:
|
||||||
|
y = timingResults.get(i).getAverage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sumX += x;
|
||||||
|
sumY += y;
|
||||||
|
sumXY += x * y;
|
||||||
|
sumXsquared += x * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate slope
|
||||||
|
// With only one measurement, the denominator will end being zero in the formula.
|
||||||
|
// Use a horizontal line in this case (or any case where the denominator is zero)
|
||||||
|
double denominator = n * sumXsquared - sumX * sumX;
|
||||||
|
if (denominator != 0) {
|
||||||
|
slope = (n * sumXY - sumX * sumY) / denominator;
|
||||||
|
} else {
|
||||||
|
slope = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate y intercept
|
||||||
|
yInt = (sumY - slope * sumX) / n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the expected y value for a given x
|
||||||
|
* @param x x coordinate of the point on the trend line
|
||||||
|
* @return expected y coordinate of this point on the trend line
|
||||||
|
*/
|
||||||
|
double getExpectedValueAt(double x) {
|
||||||
|
return (slope * x + yInt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.healthmonitor;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import org.sleuthkit.autopsy.healthmonitor.ServicesHealthMonitor.DatabaseTimingResult;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class TrendLine {
|
|
||||||
|
|
||||||
double slope;
|
|
||||||
double yInt;
|
|
||||||
|
|
||||||
TrendLine(List<DatabaseTimingResult> timingResults, TimingMetricGraphPanel.TimingMetricType timingMetricType) throws HealthMonitorException {
|
|
||||||
|
|
||||||
if((timingResults == null) || timingResults.isEmpty()) {
|
|
||||||
throw new HealthMonitorException("Can not generate trend line for empty/null data set");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate intermediate values
|
|
||||||
int n = timingResults.size();
|
|
||||||
double sumX = 0;
|
|
||||||
double sumY = 0;
|
|
||||||
double sumXY = 0;
|
|
||||||
double sumXsquared = 0;
|
|
||||||
for(int i = 0;i < n;i++) {
|
|
||||||
double x = timingResults.get(i).getTimestamp();
|
|
||||||
double y;
|
|
||||||
switch (timingMetricType) {
|
|
||||||
case MAX:
|
|
||||||
y = timingResults.get(i).getMax();
|
|
||||||
break;
|
|
||||||
case MIN:
|
|
||||||
y = timingResults.get(i).getMin();
|
|
||||||
break;
|
|
||||||
case AVERAGE:
|
|
||||||
default:
|
|
||||||
y = timingResults.get(i).getAverage();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sumX += x;
|
|
||||||
sumY += y;
|
|
||||||
sumXY += x * y;
|
|
||||||
sumXsquared += x * x;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate slope
|
|
||||||
slope = (n * sumXY - sumX * sumY) / (n * sumXsquared - sumX * sumX);
|
|
||||||
|
|
||||||
// Calculate y intercept
|
|
||||||
yInt = (sumY - slope * sumX) / n;
|
|
||||||
|
|
||||||
System.out.println("Trend line: y = " + slope + " * x + " + yInt);
|
|
||||||
}
|
|
||||||
|
|
||||||
double getExpectedValueAt(double x) {
|
|
||||||
return (slope * x + yInt);
|
|
||||||
}
|
|
||||||
}
|
|
@ -410,11 +410,18 @@ final class IngestTasksScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the file is a member of the file ingest filter that is being
|
* Ensures that all directories, files which are members of the ingest
|
||||||
* applied to the current run of ingest, checks if unallocated space
|
* file filter, and unallocated blocks (when processUnallocated is
|
||||||
* should be processed inside call to fileIsMemberOf
|
* enabled) all continue to be processed. AbstractFiles which do not
|
||||||
|
* meet one of these criteria will be skipped.
|
||||||
|
*
|
||||||
|
* An additional check to see if unallocated space should be processed
|
||||||
|
* is part of the FilesSet.fileIsMemberOf() method.
|
||||||
|
*
|
||||||
|
* This code may need to be updated when
|
||||||
|
* TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS comes into use by Autopsy.
|
||||||
*/
|
*/
|
||||||
if (!file.isDir() && task.getIngestJob().getFileIngestFilter().fileIsMemberOf(file) == null) {
|
if (!file.isDir() && !shouldBeCarved(task) && !fileAcceptedByFilter(task)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,6 +469,30 @@ final class IngestTasksScheduler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether or not a file should be carved for a data source ingest
|
||||||
|
* ingest job.
|
||||||
|
*
|
||||||
|
* @param task The file ingest task for the file.
|
||||||
|
*
|
||||||
|
* @return True or false.
|
||||||
|
*/
|
||||||
|
private static boolean shouldBeCarved(final FileIngestTask task) {
|
||||||
|
return task.getIngestJob().shouldProcessUnallocatedSpace() && task.getFile().getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether or not a file is accepted (passes) the file filter for a data
|
||||||
|
* source ingest job.
|
||||||
|
*
|
||||||
|
* @param task The file ingest task for the file.
|
||||||
|
*
|
||||||
|
* @return True or false.
|
||||||
|
*/
|
||||||
|
private static boolean fileAcceptedByFilter(final FileIngestTask task) {
|
||||||
|
return !(task.getIngestJob().getFileIngestFilter().fileIsMemberOf(task.getFile()) == null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether or not a collection of ingest tasks includes a task for a
|
* Checks whether or not a collection of ingest tasks includes a task for a
|
||||||
* given data source ingest job.
|
* given data source ingest job.
|
||||||
|
Binary file not shown.
@ -33,6 +33,7 @@ import org.sleuthkit.autopsy.casemodule.CaseActionException;
|
|||||||
import org.sleuthkit.autopsy.casemodule.CaseDetails;
|
import org.sleuthkit.autopsy.casemodule.CaseDetails;
|
||||||
import junit.framework.Test;
|
import junit.framework.Test;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.netbeans.junit.NbTestCase;
|
||||||
import org.openide.util.Exceptions;
|
import org.openide.util.Exceptions;
|
||||||
import org.python.icu.impl.Assert;
|
import org.python.icu.impl.Assert;
|
||||||
import org.sleuthkit.autopsy.casemodule.ImageDSProcessor;
|
import org.sleuthkit.autopsy.casemodule.ImageDSProcessor;
|
||||||
@ -53,11 +54,11 @@ import org.sleuthkit.datamodel.Content;
|
|||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
|
|
||||||
public class IngestFileFiltersTest extends TestCase {
|
public class IngestFileFiltersTest extends NbTestCase {
|
||||||
|
|
||||||
private static final Path CASE_DIRECTORY_PATH = Paths.get(System.getProperty("java.io.tmpdir"), "IngestFileFiltersTest");
|
private static final Path CASE_DIRECTORY_PATH = Paths.get(System.getProperty("java.io.tmpdir"), "IngestFileFiltersTest");
|
||||||
private static final File CASE_DIR = new File(CASE_DIRECTORY_PATH.toString());
|
private static final File CASE_DIR = new File(CASE_DIRECTORY_PATH.toString());
|
||||||
private static final Path IMAGE_PATH = Paths.get("test/filter_test1.img");
|
private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(),"filter_test1.img");
|
||||||
|
|
||||||
public static Test suite() {
|
public static Test suite() {
|
||||||
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestFileFiltersTest.class).
|
NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestFileFiltersTest.class).
|
||||||
@ -66,6 +67,10 @@ public class IngestFileFiltersTest extends TestCase {
|
|||||||
return conf.suite();
|
return conf.suite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IngestFileFiltersTest(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
// Delete the test directory, if it exists
|
// Delete the test directory, if it exists
|
||||||
@ -88,7 +93,7 @@ public class IngestFileFiltersTest extends TestCase {
|
|||||||
} catch (CaseActionException ex) {
|
} catch (CaseActionException ex) {
|
||||||
Exceptions.printStackTrace(ex);
|
Exceptions.printStackTrace(ex);
|
||||||
Assert.fail(ex);
|
Assert.fail(ex);
|
||||||
}
|
}
|
||||||
assertTrue(CASE_DIR.exists());
|
assertTrue(CASE_DIR.exists());
|
||||||
ImageDSProcessor dataSourceProcessor = new ImageDSProcessor();
|
ImageDSProcessor dataSourceProcessor = new ImageDSProcessor();
|
||||||
try {
|
try {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2016 Basis Technology Corp.
|
* Copyright 2018 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -23,80 +23,89 @@ import java.nio.file.Paths;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.datamodel.Image;
|
import org.sleuthkit.datamodel.Image;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A runnable that adds a raw data source to a case database.
|
* A runnable that adds a memory image data source to a case database.
|
||||||
*/
|
*/
|
||||||
final class AddMemoryImageTask implements Runnable {
|
final class AddMemoryImageTask implements Runnable {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(AddMemoryImageTask.class.getName());
|
private final static Logger logger = Logger.getLogger(AddMemoryImageTask.class.getName());
|
||||||
private final String deviceId;
|
private final String deviceId;
|
||||||
private final String imageFilePath;
|
private final String memoryImagePath;
|
||||||
private final String timeZone;
|
private final String timeZone;
|
||||||
private final List<String> pluginsToRun;
|
private final List<String> pluginsToRun;
|
||||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||||
private final DataSourceProcessorCallback callback;
|
private final DataSourceProcessorCallback callback;
|
||||||
private VolatilityProcessor volatilityProcessor = null;
|
private volatile VolatilityProcessor volatilityProcessor;
|
||||||
private boolean isCancelled = false;
|
private volatile boolean isCancelled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a runnable that adds a raw data source to a case database.
|
* Constructs a runnable that adds a memory image to a case database.
|
||||||
*
|
*
|
||||||
* @param deviceId An ASCII-printable identifier for the
|
* @param deviceId An ASCII-printable identifier for the device
|
||||||
* device associated with the data source
|
* associated with the data source that is intended
|
||||||
* that is intended to be unique across
|
* to be unique across multiple cases (e.g., a UUID).
|
||||||
* multiple cases (e.g., a UUID).
|
* @param memoryImagePath Path to the memory image file.
|
||||||
* @param imageFilePath Path to a Raw data source file.
|
* @param pluginsToRun The Volatility plugins to run.
|
||||||
* @param timeZone The time zone to use when processing dates
|
* @param timeZone The time zone to use when processing dates and
|
||||||
* and times for the image, obtained from
|
* times for the image, obtained from
|
||||||
* java.util.TimeZone.getID.
|
* java.util.TimeZone.getID.
|
||||||
* @param breakupChunks 2GB or not breakup.
|
* @param progressMonitor Progress monitor for reporting progressMonitor
|
||||||
* @param progressMonitor Progress monitor for reporting
|
* during processing.
|
||||||
* progressMonitor during processing.
|
* @param callback Callback to call when processing is done.
|
||||||
* @param callback Callback to call when processing is done.
|
|
||||||
*/
|
*/
|
||||||
AddMemoryImageTask(String deviceId, String imageFilePath, List<String> PluginsToRun, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
AddMemoryImageTask(String deviceId, String memoryImagePath, List<String> pluginsToRun, String timeZone, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.imageFilePath = imageFilePath;
|
this.memoryImagePath = memoryImagePath;
|
||||||
this.pluginsToRun = PluginsToRun;
|
this.pluginsToRun = pluginsToRun;
|
||||||
this.timeZone = timeZone;
|
this.timeZone = timeZone;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.progressMonitor = progressMonitor;
|
this.progressMonitor = progressMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a raw data source to a case database.
|
* Adds a memory image data source to a case database.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Messages({
|
||||||
|
"# {0} - exception message",
|
||||||
|
"AddMemoryImageTask_errorMessage_criticalException= Critical error: {0}",
|
||||||
|
})
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
if (isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
progressMonitor.setIndeterminate(true);
|
progressMonitor.setIndeterminate(true);
|
||||||
progressMonitor.setProgress(0);
|
progressMonitor.setProgress(0);
|
||||||
|
List<Content> dataSources = new ArrayList<>();
|
||||||
List<String> errorMessages = new ArrayList<>();
|
List<String> errorMessages = new ArrayList<>();
|
||||||
boolean criticalErrorOccurred = false;
|
boolean criticalErrorOccurred = false;
|
||||||
Image dataSource = addImageToCase(errorMessages);
|
try {
|
||||||
if (dataSource == null) {
|
Image dataSource = addImageToCase();
|
||||||
|
dataSources.add(dataSource);
|
||||||
|
volatilityProcessor = new VolatilityProcessor(memoryImagePath, dataSource, pluginsToRun, progressMonitor);
|
||||||
|
volatilityProcessor.run();
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException | VolatilityProcessor.VolatilityProcessorException ex) {
|
||||||
criticalErrorOccurred = true;
|
criticalErrorOccurred = true;
|
||||||
|
errorMessages.add(Bundle.AddMemoryImageTask_errorMessage_criticalException(ex.getLocalizedMessage()));
|
||||||
|
/*
|
||||||
|
* Log the exception as well as add it to the error messages, to
|
||||||
|
* ensure that the stack trace is not lost.
|
||||||
|
*/
|
||||||
|
logger.log(Level.SEVERE, String.format("Critical error processing memory image data source at %s for device %s", memoryImagePath, deviceId), ex);
|
||||||
}
|
}
|
||||||
/* call Volatility to process the image */
|
errorMessages.addAll(volatilityProcessor.getErrorMessages());
|
||||||
else {
|
|
||||||
if (isCancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
volatilityProcessor = new VolatilityProcessor(imageFilePath, dataSource, pluginsToRun, progressMonitor);
|
|
||||||
if (volatilityProcessor.run()) {
|
|
||||||
criticalErrorOccurred = true;
|
|
||||||
}
|
|
||||||
errorMessages.addAll(volatilityProcessor.getErrorMessages());
|
|
||||||
}
|
|
||||||
|
|
||||||
progressMonitor.setProgress(100);
|
progressMonitor.setProgress(100);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,55 +119,64 @@ final class AddMemoryImageTask implements Runnable {
|
|||||||
} else {
|
} else {
|
||||||
result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS;
|
result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS;
|
||||||
}
|
}
|
||||||
|
callback.done(result, errorMessages, dataSources);
|
||||||
callback.done(result, errorMessages, new ArrayList<>(Arrays.asList(dataSource)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to add the input image to the case.
|
* Attempts to add the input memory image to the case as a data source.
|
||||||
*
|
*
|
||||||
* @param errorMessages If there are any error messages, the error messages
|
* @return The Image object representation of the memeory image file data
|
||||||
* are added to this list for eventual return to the
|
* source.
|
||||||
* caller via the callback.
|
*
|
||||||
* @returns Image that was added to DB or null on error
|
* @throws NoCurrentCaseException If there is no current case.
|
||||||
|
* @throws TskCoreException If there is an error adding the data
|
||||||
|
* source to the case database.
|
||||||
*/
|
*/
|
||||||
@Messages({"AddMemoryImageTask.progress.add.text=Adding memory image: ",
|
@Messages({
|
||||||
"AddMemoryImageTask.image.critical.error.adding=Critical error adding ",
|
"# {0} - image file path",
|
||||||
"AddMemoryImageTask.for.device=for device ",
|
"AddMemoryImageTask_progressMessage_addingImageFile= Adding memory image {0}",
|
||||||
"AddMemoryImageTask.image.notExisting=is not existing.",
|
"# {0} - image file path",
|
||||||
"AddMemoryImageTask.image.noncritical.error.adding=Non-critical error adding "})
|
"# {1} - device id",
|
||||||
private Image addImageToCase(List<String> errorMessages) {
|
"AddMemoryImageTask_exceptionMessage_noImageFile= Memory image file {0} for device {1} does not exist"
|
||||||
progressMonitor.setProgressText(Bundle.AddMemoryImageTask_progress_add_text() + imageFilePath);
|
})
|
||||||
|
private Image addImageToCase() throws NoCurrentCaseException, TskCoreException {
|
||||||
SleuthkitCase caseDatabase = Case.getCurrentCase().getSleuthkitCase();
|
progressMonitor.setProgressText(Bundle.AddMemoryImageTask_progressMessage_addingImageFile( memoryImagePath));
|
||||||
caseDatabase.acquireExclusiveLock();
|
|
||||||
|
|
||||||
// verify it exists
|
SleuthkitCase caseDatabase = Case.getOpenCase().getSleuthkitCase();
|
||||||
File imageFile = Paths.get(imageFilePath).toFile();
|
caseDatabase.acquireSingleUserCaseWriteLock();
|
||||||
if (!imageFile.exists()) {
|
|
||||||
errorMessages.add(Bundle.AddMemoryImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddMemoryImageTask_for_device()
|
|
||||||
+ deviceId + Bundle.AddMemoryImageTask_image_notExisting());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// add it to the DB
|
/*
|
||||||
List<String> imageFilePaths = new ArrayList<>();
|
* Verify the memory image file exists.
|
||||||
imageFilePaths.add(imageFilePath);
|
*/
|
||||||
Image dataSource = caseDatabase.addImageInfo(0, imageFilePaths, timeZone); //TODO: change hard coded deviceId.
|
File imageFile = Paths.get(memoryImagePath).toFile();
|
||||||
|
if (!imageFile.exists()) {
|
||||||
|
throw new TskCoreException(Bundle.AddMemoryImageTask_exceptionMessage_noImageFile(memoryImagePath, deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the data source.
|
||||||
|
*
|
||||||
|
* NOTE: The object id for device passed to
|
||||||
|
* SleuthkitCase.addImageInfo is hard-coded to zero for now. This
|
||||||
|
* will need to be changed when a Device abstraction is added to the
|
||||||
|
* SleuthKit data model.
|
||||||
|
*/
|
||||||
|
Image dataSource = caseDatabase.addImageInfo(0, new ArrayList<>(Arrays.asList(memoryImagePath)), timeZone);
|
||||||
return dataSource;
|
return dataSource;
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
errorMessages.add(Bundle.AddMemoryImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddMemoryImageTask_for_device() + deviceId + ":" + ex.getLocalizedMessage());
|
|
||||||
return null;
|
|
||||||
} finally {
|
} finally {
|
||||||
caseDatabase.releaseExclusiveLock();
|
caseDatabase.releaseSingleUserCaseWriteLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests cancellation of this task by setting a cancelled flag.
|
||||||
|
*/
|
||||||
void cancelTask() {
|
void cancelTask() {
|
||||||
isCancelled = true;
|
isCancelled = true;
|
||||||
if (volatilityProcessor != null) {
|
if (volatilityProcessor != null) {
|
||||||
volatilityProcessor.cancel();
|
volatilityProcessor.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2016 Basis Technology Corp.
|
* Copyright 2018 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -37,11 +37,13 @@ import javax.swing.table.AbstractTableModel;
|
|||||||
import javax.swing.table.TableColumn;
|
import javax.swing.table.TableColumn;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||||
import org.sleuthkit.autopsy.coreutils.PathValidator;
|
import org.sleuthkit.autopsy.coreutils.PathValidator;
|
||||||
|
|
||||||
final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L; //default
|
private static final long serialVersionUID = 1L; //default
|
||||||
private final String PROP_LASTINPUT_PATH = "LBL_LastInputFile_PATH";
|
private final String PROP_LASTINPUT_PATH = "LBL_LastInputFile_PATH";
|
||||||
private final JFileChooser fc = new JFileChooser();
|
private final JFileChooser fc = new JFileChooser();
|
||||||
@ -52,14 +54,14 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
|||||||
private final List<String> PluginListNames = new ArrayList<>();
|
private final List<String> PluginListNames = new ArrayList<>();
|
||||||
private final Map<String, Boolean> pluginListStates = new HashMap<>(); // is set by listeners when users select and deselect items
|
private final Map<String, Boolean> pluginListStates = new HashMap<>(); // is set by listeners when users select and deselect items
|
||||||
private final Boolean isEnabled = true;
|
private final Boolean isEnabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new MemoryDSInputPanel panel for user input
|
* Creates new MemoryDSInputPanel panel for user input
|
||||||
*/
|
*/
|
||||||
private MemoryDSInputPanel(String context) {
|
private MemoryDSInputPanel(String context) {
|
||||||
this.pluginList = new String[]{"amcache","cmdline","cmdscan","consoles","malfind","netscan","notepad","pslist","psxview","shellbags","shimcache","shutdown","userassist", "apihooks","connscan","devicetree","dlllist","envars","filescan","gahti","getservicesids","getsids","handles","hashdump","hivelist","hivescan","impscan","ldrmodules","lsadump","modules","mutantscan","privs","psscan","pstree","sockets","svcscan","shimcache","timeliner","unloadedmodules","userhandles","vadinfo","verinfo"};
|
this.pluginList = new String[]{"amcache", "cmdline", "cmdscan", "consoles", "malfind", "netscan", "notepad", "pslist", "psxview", "shellbags", "shimcache", "shutdown", "userassist", "apihooks", "connscan", "devicetree", "dlllist", "envars", "filescan", "gahti", "getservicesids", "getsids", "handles", "hashdump", "hivelist", "hivescan", "impscan", "ldrmodules", "lsadump", "modules", "mutantscan", "privs", "psscan", "pstree", "sockets", "svcscan", "shimcache", "timeliner", "unloadedmodules", "userhandles", "vadinfo", "verinfo"};
|
||||||
Arrays.sort(this.pluginList);
|
Arrays.sort(this.pluginList);
|
||||||
|
|
||||||
initComponents();
|
initComponents();
|
||||||
|
|
||||||
errorLabel.setVisible(false);
|
errorLabel.setVisible(false);
|
||||||
@ -91,7 +93,7 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
|||||||
private void postInit() {
|
private void postInit() {
|
||||||
pathTextField.getDocument().addDocumentListener(this);
|
pathTextField.getDocument().addDocumentListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void customizePluginListTable() {
|
private void customizePluginListTable() {
|
||||||
PluginList.setModel(tableModel);
|
PluginList.setModel(tableModel);
|
||||||
PluginList.setTableHeader(null);
|
PluginList.setTableHeader(null);
|
||||||
@ -135,14 +137,14 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
|||||||
// set the selected timezone
|
// set the selected timezone
|
||||||
timeZoneComboBox.setSelectedItem(formatted);
|
timeZoneComboBox.setSelectedItem(formatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createVolatilityVersionList() {
|
private void createVolatilityVersionList() {
|
||||||
|
|
||||||
volExecutableComboBox.addItem("2.6");
|
volExecutableComboBox.addItem("2.6");
|
||||||
volExecutableComboBox.addItem("2.5");
|
volExecutableComboBox.addItem("2.5");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createPluginList() {
|
private void createPluginList() {
|
||||||
PluginListNames.clear();
|
PluginListNames.clear();
|
||||||
pluginListStates.clear();
|
pluginListStates.clear();
|
||||||
@ -150,19 +152,19 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
|||||||
// if the config file doesn't exist, then set them all to enabled
|
// if the config file doesn't exist, then set them all to enabled
|
||||||
boolean allEnabled = !ModuleSettings.configExists(this.contextName);
|
boolean allEnabled = !ModuleSettings.configExists(this.contextName);
|
||||||
Map<String, String> pluginMap = ModuleSettings.getConfigSettings(this.contextName);
|
Map<String, String> pluginMap = ModuleSettings.getConfigSettings(this.contextName);
|
||||||
|
|
||||||
for (String plugin : pluginList) {
|
for (String plugin : pluginList) {
|
||||||
PluginListNames.add(plugin);
|
PluginListNames.add(plugin);
|
||||||
if (allEnabled)
|
if (allEnabled) {
|
||||||
pluginListStates.put(plugin, true);
|
pluginListStates.put(plugin, true);
|
||||||
else
|
} else {
|
||||||
pluginListStates.put(plugin, pluginMap.containsKey(plugin));
|
pluginListStates.put(plugin, pluginMap.containsKey(plugin));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tableModel.fireTableDataChanged();
|
tableModel.fireTableDataChanged();
|
||||||
//this.tableModel = pluginsToRun.getModel();
|
//this.tableModel = pluginsToRun.getModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called from within the constructor to initialize the form.
|
* 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
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
@ -282,18 +284,18 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
|||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
|
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
|
||||||
String oldText = pathTextField.getText();
|
String oldText = pathTextField.getText();
|
||||||
// set the current directory of the FileChooser if the ImagePath Field is valid
|
// set the current directory of the FileChooser if the ImagePath Field is valid
|
||||||
File currentDir = new File(oldText);
|
File currentDir = new File(oldText);
|
||||||
if (currentDir.exists()) {
|
if (currentDir.exists()) {
|
||||||
fc.setCurrentDirectory(currentDir);
|
fc.setCurrentDirectory(currentDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
int retval = fc.showOpenDialog(this);
|
int retval = fc.showOpenDialog(this);
|
||||||
if (retval == JFileChooser.APPROVE_OPTION) {
|
if (retval == JFileChooser.APPROVE_OPTION) {
|
||||||
String path = fc.getSelectedFile().getPath();
|
String path = fc.getSelectedFile().getPath();
|
||||||
pathTextField.setText(path);
|
pathTextField.setText(path);
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_browseButtonActionPerformed
|
}//GEN-LAST:event_browseButtonActionPerformed
|
||||||
|
|
||||||
private void volExecutableComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_volExecutableComboBoxActionPerformed
|
private void volExecutableComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_volExecutableComboBoxActionPerformed
|
||||||
@ -322,7 +324,7 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
|||||||
String getImageFilePath() {
|
String getImageFilePath() {
|
||||||
return pathTextField.getText();
|
return pathTextField.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> getPluginsToRun() {
|
List<String> getPluginsToRun() {
|
||||||
List<String> enabledPlugins = new ArrayList<>();
|
List<String> enabledPlugins = new ArrayList<>();
|
||||||
Map<String, String> pluginMap = new HashMap<>();
|
Map<String, String> pluginMap = new HashMap<>();
|
||||||
@ -332,7 +334,7 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
|||||||
pluginMap.put(plugin, "");
|
pluginMap.put(plugin, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleSettings.setConfigSettings(this.contextName, pluginMap);
|
ModuleSettings.setConfigSettings(this.contextName, pluginMap);
|
||||||
// @@ Could return keys of set
|
// @@ Could return keys of set
|
||||||
return enabledPlugins;
|
return enabledPlugins;
|
||||||
@ -374,11 +376,19 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
|||||||
*
|
*
|
||||||
* @param path Absolute path to the selected data source
|
* @param path Absolute path to the selected data source
|
||||||
*/
|
*/
|
||||||
@Messages({"MemoryDSInputPanel.error.text=Path to multi-user data source is on \"C:\" drive"})
|
@Messages({
|
||||||
|
"MemoryDSInputPanel_errorMsg_noOpenCase=No open case",
|
||||||
|
"MemoryDSInputPanel_errorMsg_dataSourcePathOnCdrive=Path to multi-user data source is on \"C:\" drive"
|
||||||
|
})
|
||||||
private void warnIfPathIsInvalid(String path) {
|
private void warnIfPathIsInvalid(String path) {
|
||||||
if (!PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) {
|
try {
|
||||||
|
if (!PathValidator.isValid(path, Case.getOpenCase().getCaseType())) {
|
||||||
|
errorLabel.setVisible(true);
|
||||||
|
errorLabel.setText(Bundle.MemoryDSInputPanel_errorMsg_dataSourcePathOnCdrive());
|
||||||
|
}
|
||||||
|
} catch (NoCurrentCaseException unused) {
|
||||||
errorLabel.setVisible(true);
|
errorLabel.setVisible(true);
|
||||||
errorLabel.setText(Bundle.MemoryDSInputPanel_error_text());
|
errorLabel.setText(Bundle.MemoryDSInputPanel_errorMsg_dataSourcePathOnCdrive());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,5 +480,4 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2016 Basis Technology Corp.
|
* Copyright 2018 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -28,21 +28,21 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback
|
|||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A MEmory data source processor that implements the DataSourceProcessor service
|
* A memory image data source processor that implements the DataSourceProcessor
|
||||||
* provider interface to allow integration with the add data source wizard. It
|
* service provider interface to allow integration with the Add Data Source
|
||||||
* also provides a run method overload to allow it to be used independently of
|
* wizard. It also provides a run method overload to allow it to be used
|
||||||
* the wizard.
|
* independently of the wizard.
|
||||||
*/
|
*/
|
||||||
@ServiceProvider(service = DataSourceProcessor.class)
|
@ServiceProvider(service = DataSourceProcessor.class)
|
||||||
public class MemoryDSProcessor implements DataSourceProcessor {
|
public class MemoryDSProcessor implements DataSourceProcessor {
|
||||||
|
|
||||||
private final MemoryDSInputPanel configPanel;
|
private final MemoryDSInputPanel configPanel;
|
||||||
private AddMemoryImageTask addImageTask = null;
|
private AddMemoryImageTask addImageTask;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Constructs a Memory data source processor that implements the
|
* Constructs a memory data source processor that implements the
|
||||||
* DataSourceProcessor service provider interface to allow integration with
|
* DataSourceProcessor service provider interface to allow integration with
|
||||||
* the add data source wizard. It also provides a run method overload to
|
* the Add Data source wizard. It also provides a run method overload to
|
||||||
* allow it to be used independently of the wizard.
|
* allow it to be used independently of the wizard.
|
||||||
*/
|
*/
|
||||||
public MemoryDSProcessor() {
|
public MemoryDSProcessor() {
|
||||||
@ -117,37 +117,40 @@ public class MemoryDSProcessor implements DataSourceProcessor {
|
|||||||
@Override
|
@Override
|
||||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
configPanel.storeSettings();
|
configPanel.storeSettings();
|
||||||
run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getPluginsToRun(), configPanel.getTimeZone(), 0, progressMonitor, callback);
|
run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getPluginsToRun(), configPanel.getTimeZone(), progressMonitor, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a "memory" data source to the case database using a background task in
|
* Adds a memory image data source to the case database using a background
|
||||||
* a separate thread and the given settings instead of those provided by the
|
* task in a separate thread and the given settings instead of those
|
||||||
* selection and configuration panel. Returns as soon as the background task
|
* provided by the selection and configuration panel. Returns as soon as the
|
||||||
* is started and uses the callback object to signal task completion and
|
* background task is started and uses the callback object to signal task
|
||||||
* return results.
|
* completion and return results.
|
||||||
*
|
*
|
||||||
* @param deviceId An ASCII-printable identifier for the device
|
* @param deviceId An ASCII-printable identifier for the device
|
||||||
* associated with the data source that is
|
* associated with the data source that is intended
|
||||||
* intended to be unique across multiple cases
|
* to be unique across multiple cases (e.g., a UUID).
|
||||||
* (e.g., a UUID).
|
* @param memoryImagePath Path to the memory image file.
|
||||||
* @param imageFilePath Path to the image file.
|
* @param pluginsToRun The Volatility plugins to run.
|
||||||
* @param timeZone The time zone to use when processing dates
|
* @param timeZone The time zone to use when processing dates and
|
||||||
* and times for the image, obtained from
|
* times for the image, obtained from
|
||||||
* java.util.TimeZone.getID.
|
* java.util.TimeZone.getID.
|
||||||
* @param chunkSize The maximum size of each chunk of the raw
|
* @param progressMonitor Progress monitor for reporting progress during
|
||||||
* data source as it is divided up into virtual
|
* processing.
|
||||||
* unallocated space files.
|
* @param callback Callback to call when processing is done.
|
||||||
* @param progressMonitor Progress monitor for reporting progress
|
|
||||||
* during processing.
|
|
||||||
* @param callback Callback to call when processing is done.
|
|
||||||
*/
|
*/
|
||||||
private void run(String deviceId, String imageFilePath, List<String> pluginsToRun, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
private void run(String deviceId, String memoryImagePath, List<String> pluginsToRun, String timeZone, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
addImageTask = new AddMemoryImageTask(deviceId, imageFilePath, pluginsToRun, timeZone, 0, progressMonitor, callback);
|
addImageTask = new AddMemoryImageTask(deviceId, memoryImagePath, pluginsToRun, timeZone, progressMonitor, callback);
|
||||||
new Thread(addImageTask).start();
|
new Thread(addImageTask).start();
|
||||||
//new Thread(new AddLocalFilesTask(deviceId, rootVirtualDirectoryName, localFilePaths, progressMonitor, callback)).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests cancellation of the background task that adds a data source to
|
||||||
|
* the case database, after the task is started using the run method. This
|
||||||
|
* is a "best effort" cancellation, with no guarantees that the case
|
||||||
|
* database will be unchanged. If cancellation succeeded, the list of new
|
||||||
|
* data sources returned by the background task will be empty.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
if (addImageTask != null) {
|
if (addImageTask != null) {
|
||||||
@ -165,4 +168,3 @@ public class MemoryDSProcessor implements DataSourceProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,7 @@ import org.apache.solr.common.SolrInputDocument;
|
|||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||||
import org.sleuthkit.autopsy.healthmonitor.ServicesHealthMonitor;
|
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor;
|
||||||
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||||
import org.sleuthkit.autopsy.keywordsearch.Chunker.Chunk;
|
import org.sleuthkit.autopsy.keywordsearch.Chunker.Chunk;
|
||||||
@ -237,9 +237,9 @@ class Ingester {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
//TODO: consider timeout thread, or vary socket timeout based on size of indexed content
|
//TODO: consider timeout thread, or vary socket timeout based on size of indexed content
|
||||||
TimingMetric metric = ServicesHealthMonitor.getTimingMetric("Solr: Index chunk");
|
TimingMetric metric = EnterpriseHealthMonitor.getTimingMetric("Solr: Index chunk");
|
||||||
solrServer.addDocument(updateDoc);
|
solrServer.addDocument(updateDoc);
|
||||||
ServicesHealthMonitor.submitTimingMetric(metric);
|
EnterpriseHealthMonitor.submitTimingMetric(metric);
|
||||||
uncommitedIngests = true;
|
uncommitedIngests = true;
|
||||||
|
|
||||||
} catch (KeywordSearchModuleException | NoOpenCoreException ex) {
|
} catch (KeywordSearchModuleException | NoOpenCoreException ex) {
|
||||||
|
@ -645,18 +645,18 @@ final class RegexQuery implements KeywordSearchQuery {
|
|||||||
*/
|
*/
|
||||||
static private void addAttributeIfNotAlreadyCaptured(Map<BlackboardAttribute.Type, BlackboardAttribute> attributeMap, ATTRIBUTE_TYPE attrType, String groupName, Matcher matcher) {
|
static private void addAttributeIfNotAlreadyCaptured(Map<BlackboardAttribute.Type, BlackboardAttribute> attributeMap, ATTRIBUTE_TYPE attrType, String groupName, Matcher matcher) {
|
||||||
BlackboardAttribute.Type type = new BlackboardAttribute.Type(attrType);
|
BlackboardAttribute.Type type = new BlackboardAttribute.Type(attrType);
|
||||||
attributeMap.computeIfAbsent(type, t -> {
|
|
||||||
|
if( ! attributeMap.containsKey(type)) {
|
||||||
String value = matcher.group(groupName);
|
String value = matcher.group(groupName);
|
||||||
if (attrType.equals(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)) {
|
if (attrType.equals(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)) {
|
||||||
attributeMap.put(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD),
|
attributeMap.put(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD),
|
||||||
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, value));
|
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, value));
|
||||||
value = CharMatcher.anyOf(" -").removeFrom(value);
|
value = CharMatcher.anyOf(" -").removeFrom(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(value)) {
|
if (StringUtils.isNotBlank(value)) {
|
||||||
return new BlackboardAttribute(attrType, MODULE_NAME, value);
|
attributeMap.put(type, new BlackboardAttribute(attrType, MODULE_NAME, value));
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ import org.sleuthkit.autopsy.core.UserPreferences;
|
|||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||||
import org.sleuthkit.autopsy.healthmonitor.ServicesHealthMonitor;
|
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor;
|
||||||
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
||||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException;
|
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
@ -775,9 +775,9 @@ public class Server {
|
|||||||
IndexingServerProperties properties = getMultiUserServerProperties(theCase.getCaseDirectory());
|
IndexingServerProperties properties = getMultiUserServerProperties(theCase.getCaseDirectory());
|
||||||
currentSolrServer = new HttpSolrServer("http://" + properties.getHost() + ":" + properties.getPort() + "/solr"); //NON-NLS
|
currentSolrServer = new HttpSolrServer("http://" + properties.getHost() + ":" + properties.getPort() + "/solr"); //NON-NLS
|
||||||
}
|
}
|
||||||
TimingMetric metric = ServicesHealthMonitor.getTimingMetric("Solr: Connectivity check");
|
TimingMetric metric = EnterpriseHealthMonitor.getTimingMetric("Solr: Connectivity check");
|
||||||
connectToSolrServer(currentSolrServer);
|
connectToSolrServer(currentSolrServer);
|
||||||
ServicesHealthMonitor.submitTimingMetric(metric);
|
EnterpriseHealthMonitor.submitTimingMetric(metric);
|
||||||
|
|
||||||
} catch (SolrServerException | IOException ex) {
|
} catch (SolrServerException | IOException ex) {
|
||||||
throw new KeywordSearchModuleException(NbBundle.getMessage(Server.class, "Server.connect.exception.msg", ex.getLocalizedMessage()), ex);
|
throw new KeywordSearchModuleException(NbBundle.getMessage(Server.class, "Server.connect.exception.msg", ex.getLocalizedMessage()), ex);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user