Merge branch 'develop' of https://github.com/sleuthkit/autopsy into develop
@ -47,7 +47,7 @@ public interface EventBundle<ParentType extends EventBundle<?>> {
|
||||
|
||||
Optional<ParentType> getParentBundle();
|
||||
|
||||
default long getCount() {
|
||||
default int getCount() {
|
||||
return getEventIDs().size();
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,9 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.datamodel;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@ -89,28 +89,28 @@ public class EventCluster implements EventBundle<EventStripe> {
|
||||
/**
|
||||
* the set of ids of the clustered events
|
||||
*/
|
||||
final private Set<Long> eventIDs;
|
||||
final private ImmutableSet<Long> eventIDs;
|
||||
|
||||
/**
|
||||
* the ids of the subset of clustered events that have at least one tag
|
||||
* applied to them
|
||||
*/
|
||||
private final Set<Long> tagged;
|
||||
private final ImmutableSet<Long> tagged;
|
||||
|
||||
/**
|
||||
* the ids of the subset of clustered events that have at least one hash set
|
||||
* hit
|
||||
*/
|
||||
private final Set<Long> hashHits;
|
||||
private final ImmutableSet<Long> hashHits;
|
||||
|
||||
private EventCluster(Interval spanningInterval, EventType type, Set<Long> eventIDs, Set<Long> hashHits, Set<Long> tagged, String description, DescriptionLoD lod, EventStripe parent) {
|
||||
|
||||
this.span = spanningInterval;
|
||||
this.type = type;
|
||||
this.hashHits = hashHits;
|
||||
this.tagged = tagged;
|
||||
this.hashHits = ImmutableSet.copyOf(hashHits);
|
||||
this.tagged = ImmutableSet.copyOf(tagged);
|
||||
this.description = description;
|
||||
this.eventIDs = eventIDs;
|
||||
this.eventIDs = ImmutableSet.copyOf(eventIDs);
|
||||
this.lod = lod;
|
||||
this.parent = parent;
|
||||
}
|
||||
@ -139,18 +139,21 @@ public class EventCluster implements EventBundle<EventStripe> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getEventIDs() {
|
||||
return Collections.unmodifiableSet(eventIDs);
|
||||
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
||||
public ImmutableSet<Long> getEventIDs() {
|
||||
return eventIDs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getEventIDsWithHashHits() {
|
||||
return Collections.unmodifiableSet(hashHits);
|
||||
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
||||
public ImmutableSet<Long> getEventIDsWithHashHits() {
|
||||
return hashHits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getEventIDsWithTags() {
|
||||
return Collections.unmodifiableSet(tagged);
|
||||
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
||||
public ImmutableSet<Long> getEventIDsWithTags() {
|
||||
return tagged;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,13 +19,11 @@
|
||||
package org.sleuthkit.autopsy.timeline.datamodel;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.Collections;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import org.python.google.common.base.Objects;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
||||
@ -50,7 +48,7 @@ public final class EventStripe implements EventBundle<EventCluster> {
|
||||
|
||||
private final EventCluster parent;
|
||||
|
||||
private final SortedSet<EventCluster> clusters = new TreeSet<>(Comparator.comparing(EventCluster::getStartMillis));
|
||||
private final ImmutableSortedSet<EventCluster> clusters;
|
||||
|
||||
/**
|
||||
* the type of all the events
|
||||
@ -70,59 +68,70 @@ public final class EventStripe implements EventBundle<EventCluster> {
|
||||
/**
|
||||
* the set of ids of the events
|
||||
*/
|
||||
private final Set<Long> eventIDs = new HashSet<>();
|
||||
private final ImmutableSet<Long> eventIDs;
|
||||
|
||||
/**
|
||||
* the ids of the subset of events that have at least one tag applied to
|
||||
* them
|
||||
*/
|
||||
private final Set<Long> tagged = new HashSet<>();
|
||||
private final ImmutableSet<Long> tagged;
|
||||
|
||||
/**
|
||||
* the ids of the subset of events that have at least one hash set hit
|
||||
*/
|
||||
private final Set<Long> hashHits = new HashSet<>();
|
||||
private final ImmutableSet<Long> hashHits;
|
||||
|
||||
public EventStripe withParent(EventCluster parent) {
|
||||
EventStripe eventStripe = new EventStripe(parent, this.type, this.description, this.lod);
|
||||
eventStripe.clusters.addAll(clusters);
|
||||
eventStripe.eventIDs.addAll(eventIDs);
|
||||
eventStripe.tagged.addAll(tagged);
|
||||
eventStripe.hashHits.addAll(hashHits);
|
||||
EventStripe eventStripe = new EventStripe(parent, this.type, this.description, this.lod, clusters, eventIDs, tagged, hashHits);
|
||||
return eventStripe;
|
||||
}
|
||||
|
||||
private EventStripe(EventCluster parent, EventType type, String description, DescriptionLoD lod) {
|
||||
private EventStripe(EventCluster parent, EventType type, String description, DescriptionLoD lod, SortedSet<EventCluster> clusters, ImmutableSet<Long> eventIDs, ImmutableSet<Long> tagged, ImmutableSet<Long> hashHits) {
|
||||
this.parent = parent;
|
||||
this.type = type;
|
||||
this.description = description;
|
||||
this.lod = lod;
|
||||
this.clusters = ImmutableSortedSet.copyOf(Comparator.comparing(EventCluster::getStartMillis), clusters);
|
||||
|
||||
this.eventIDs = eventIDs;
|
||||
this.tagged = tagged;
|
||||
this.hashHits = hashHits;
|
||||
}
|
||||
|
||||
public EventStripe(EventCluster cluster, EventCluster parent) {
|
||||
clusters.add(cluster);
|
||||
this.clusters = ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis))
|
||||
.add(cluster).build();
|
||||
|
||||
type = cluster.getEventType();
|
||||
description = cluster.getDescription();
|
||||
lod = cluster.getDescriptionLoD();
|
||||
eventIDs.addAll(cluster.getEventIDs());
|
||||
tagged.addAll(cluster.getEventIDsWithTags());
|
||||
hashHits.addAll(cluster.getEventIDsWithHashHits());
|
||||
eventIDs = cluster.getEventIDs();
|
||||
tagged = cluster.getEventIDsWithTags();
|
||||
hashHits = cluster.getEventIDsWithHashHits();
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
private EventStripe(EventStripe u, EventStripe v) {
|
||||
clusters.addAll(u.clusters);
|
||||
clusters.addAll(v.clusters);
|
||||
clusters = ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis))
|
||||
.addAll(u.getClusters())
|
||||
.addAll(v.getClusters())
|
||||
.build();
|
||||
|
||||
type = u.getEventType();
|
||||
description = u.getDescription();
|
||||
lod = u.getDescriptionLoD();
|
||||
eventIDs.addAll(u.getEventIDs());
|
||||
eventIDs.addAll(v.getEventIDs());
|
||||
tagged.addAll(u.getEventIDsWithTags());
|
||||
tagged.addAll(v.getEventIDsWithTags());
|
||||
hashHits.addAll(u.getEventIDsWithHashHits());
|
||||
hashHits.addAll(v.getEventIDsWithHashHits());
|
||||
eventIDs = ImmutableSet.<Long>builder()
|
||||
.addAll(u.getEventIDs())
|
||||
.addAll(v.getEventIDs())
|
||||
.build();
|
||||
tagged = ImmutableSet.<Long>builder()
|
||||
.addAll(u.getEventIDsWithTags())
|
||||
.addAll(v.getEventIDsWithTags())
|
||||
.build();
|
||||
hashHits = ImmutableSet.<Long>builder()
|
||||
.addAll(u.getEventIDsWithHashHits())
|
||||
.addAll(v.getEventIDsWithHashHits())
|
||||
.build();
|
||||
parent = u.getParentBundle().orElse(v.getParentBundle().orElse(null));
|
||||
}
|
||||
|
||||
@ -147,18 +156,21 @@ public final class EventStripe implements EventBundle<EventCluster> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getEventIDs() {
|
||||
return Collections.unmodifiableSet(eventIDs);
|
||||
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
||||
public ImmutableSet<Long> getEventIDs() {
|
||||
return eventIDs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getEventIDsWithHashHits() {
|
||||
return Collections.unmodifiableSet(hashHits);
|
||||
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
||||
public ImmutableSet<Long> getEventIDsWithHashHits() {
|
||||
return hashHits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getEventIDsWithTags() {
|
||||
return Collections.unmodifiableSet(tagged);
|
||||
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
||||
public ImmutableSet<Long> getEventIDsWithTags() {
|
||||
return tagged;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -172,14 +184,13 @@ public final class EventStripe implements EventBundle<EventCluster> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet< EventCluster> getClusters() {
|
||||
return Collections.unmodifiableSortedSet(clusters);
|
||||
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
||||
public ImmutableSortedSet< EventCluster> getClusters() {
|
||||
return clusters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EventStripe{" + "description=" + description + ", eventIDs=" + eventIDs.size() + '}';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -32,7 +31,10 @@ import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Insets;
|
||||
@ -120,7 +122,7 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
|
||||
final Background defaultBackground;
|
||||
final Color evtColor;
|
||||
|
||||
final List<ParentNodeType> subNodes = new ArrayList<>();
|
||||
final ObservableList<ParentNodeType> subNodes = FXCollections.observableArrayList();
|
||||
final Pane subNodePane = new Pane();
|
||||
final Label descrLabel = new Label();
|
||||
final Label countLabel = new Label();
|
||||
@ -152,7 +154,11 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
|
||||
|
||||
setBackground(defaultBackground);
|
||||
setAlignment(Pos.TOP_LEFT);
|
||||
|
||||
setMaxWidth(USE_PREF_SIZE);
|
||||
infoHBox.setMaxWidth(USE_PREF_SIZE);
|
||||
subNodePane.setPrefWidth(USE_COMPUTED_SIZE);
|
||||
subNodePane.setMinWidth(USE_PREF_SIZE);
|
||||
subNodePane.setMaxWidth(USE_PREF_SIZE);
|
||||
/*
|
||||
* This triggers the layout when a mousover causes the action buttons to
|
||||
* interesect with another node, forcing it down.
|
||||
@ -185,6 +191,8 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
|
||||
setOnMouseClicked(new ClickHandler());
|
||||
descVisibility.addListener(observable -> setDescriptionVisibiltiyImpl(descVisibility.get()));
|
||||
descVisibility.set(DescriptionVisibility.SHOWN); //trigger listener for initial value
|
||||
|
||||
Bindings.bindContent(subNodePane.getChildren(), subNodes);
|
||||
}
|
||||
|
||||
final DescriptionLoD getDescriptionLoD() {
|
||||
@ -342,6 +350,8 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
|
||||
super.layoutChildren();
|
||||
}
|
||||
|
||||
abstract ParentNodeType createChildNode(ParentType rawChild);
|
||||
|
||||
/**
|
||||
* @param w the maximum width the description label should have
|
||||
*/
|
||||
|
@ -25,7 +25,10 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import static java.util.Objects.nonNull;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.event.EventHandler;
|
||||
@ -45,7 +48,9 @@ import org.controlsfx.control.action.ActionUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Interval;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.LoggedTask;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
|
||||
import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter;
|
||||
@ -63,6 +68,25 @@ import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
||||
final public class EventClusterNode extends EventBundleNodeBase<EventCluster, EventStripe, EventStripeNode> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(EventClusterNode.class.getName());
|
||||
/**
|
||||
* Use this recursive function to flatten a tree of nodes into an single
|
||||
* stream. More specifically it takes an EventStripeNode and produces a
|
||||
* stream of EventStripes conaiting the stripes for the given node and all
|
||||
* child eventStripes, ignoring intervening EventCluster nodes.
|
||||
*
|
||||
* @see
|
||||
* #loadSubBundles(org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD.RelativeDetail)
|
||||
* for usage
|
||||
*/
|
||||
private final static Function<EventStripeNode, Stream<EventStripe>> stripeFlattener = new Function<EventStripeNode, Stream<EventStripe>>() {
|
||||
@Override
|
||||
public Stream<EventStripe> apply(EventStripeNode node) {
|
||||
return Stream.concat(
|
||||
Stream.of(node.getEventStripe()),
|
||||
node.getSubNodes().stream().flatMap(clusterNode -> clusterNode.getSubNodes().stream().flatMap(this)));
|
||||
}
|
||||
};
|
||||
|
||||
private static final BorderWidths CLUSTER_BORDER_WIDTHS = new BorderWidths(2, 1, 2, 1);
|
||||
private static final Image PLUS = new Image("/org/sleuthkit/autopsy/timeline/images/plus-button.png"); // NON-NLS //NOI18N
|
||||
private static final Image MINUS = new Image("/org/sleuthkit/autopsy/timeline/images/minus-button.png"); // NON-NLS //NOI18N
|
||||
@ -89,6 +113,7 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
|
||||
subNodePane.setBorder(clusterBorder);
|
||||
subNodePane.setBackground(defaultBackground);
|
||||
subNodePane.setMinWidth(1);
|
||||
subNodePane.setMaxWidth(USE_PREF_SIZE);
|
||||
setMinHeight(24);
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
@ -118,7 +143,7 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
|
||||
|
||||
@Override
|
||||
void setDescriptionVisibiltiyImpl(DescriptionVisibility descrVis) {
|
||||
final int size = getEventBundle().getEventIDs().size();
|
||||
final int size = getEventCluster().getCount();
|
||||
switch (descrVis) {
|
||||
case HIDDEN:
|
||||
countLabel.setText("");
|
||||
@ -142,10 +167,10 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
|
||||
* @param expand
|
||||
*/
|
||||
@NbBundle.Messages(value = "EventStripeNode.loggedTask.name=Load sub clusters")
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private synchronized void loadSubBundles(DescriptionLoD.RelativeDetail relativeDetail) {
|
||||
chart.setCursor(Cursor.WAIT);
|
||||
chart.getEventStripes().removeAll(Lists.transform(subNodes, EventStripeNode::getEventStripe));
|
||||
subNodes.clear();
|
||||
|
||||
|
||||
/*
|
||||
* make new ZoomParams to query with
|
||||
@ -159,14 +184,10 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
|
||||
final EventTypeZoomLevel eventTypeZoomLevel = eventsModel.eventTypeZoomProperty().get();
|
||||
final ZoomParams zoomParams = new ZoomParams(subClusterSpan, eventTypeZoomLevel, subClusterFilter, getDescriptionLoD());
|
||||
|
||||
Task<List<EventStripe>> loggedTask = new Task<List<EventStripe>>() {
|
||||
Task<List<EventStripe>> loggedTask = new LoggedTask<List<EventStripe>>(Bundle.EventStripeNode_loggedTask_name(), false) {
|
||||
|
||||
private volatile DescriptionLoD loadedDescriptionLoD = getDescriptionLoD().withRelativeDetail(relativeDetail);
|
||||
|
||||
{
|
||||
updateTitle(Bundle.EventStripeNode_loggedTask_name());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<EventStripe> call() throws Exception {
|
||||
List<EventStripe> bundles;
|
||||
@ -182,7 +203,9 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
|
||||
} while (bundles.size() == 1 && nonNull(next));
|
||||
|
||||
// return list of EventStripes representing sub-bundles
|
||||
return Lists.transform(bundles, eventStripe -> eventStripe.withParent(getEventCluster()));
|
||||
return bundles.stream()
|
||||
.map(eventStripe -> eventStripe.withParent(getEventCluster()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -190,14 +213,16 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
|
||||
try {
|
||||
List<EventStripe> bundles = get();
|
||||
|
||||
//clear the existing subnodes
|
||||
List<EventStripe> transform = subNodes.stream().flatMap(stripeFlattener).collect(Collectors.toList());
|
||||
chart.getEventStripes().removeAll(transform);
|
||||
subNodes.clear();
|
||||
if (bundles.isEmpty()) {
|
||||
subNodePane.getChildren().clear();
|
||||
getChildren().setAll(subNodePane, infoHBox);
|
||||
descLOD.set(getEventBundle().getDescriptionLoD());
|
||||
} else {
|
||||
chart.getEventStripes().addAll(bundles);
|
||||
subNodes.addAll(Lists.transform(bundles, EventClusterNode.this::createStripeNode));
|
||||
subNodePane.getChildren().setAll(subNodes);
|
||||
subNodes.addAll(Lists.transform(bundles, EventClusterNode.this::createChildNode));
|
||||
getChildren().setAll(new VBox(infoHBox, subNodePane));
|
||||
descLOD.set(loadedDescriptionLoD);
|
||||
}
|
||||
@ -214,7 +239,8 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
|
||||
chart.getController().monitorTask(loggedTask);
|
||||
}
|
||||
|
||||
private EventStripeNode createStripeNode(EventStripe stripe) {
|
||||
@Override
|
||||
EventStripeNode createChildNode(EventStripe stripe) {
|
||||
return new EventStripeNode(chart, stripe, this);
|
||||
}
|
||||
|
||||
|
@ -388,7 +388,7 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
|
||||
* @return all the nodes that pass the given predicate
|
||||
*/
|
||||
synchronized Iterable<EventBundleNodeBase<?, ?, ?>> getNodes(Predicate<EventBundleNodeBase<?, ?, ?>> p) {
|
||||
//use this recursive function to flatten the tree of nodes into an iterable.
|
||||
//use this recursive function to flatten the tree of nodes into an single stream.
|
||||
Function<EventBundleNodeBase<?, ?, ?>, Stream<EventBundleNodeBase<?, ?, ?>>> stripeFlattener =
|
||||
new Function<EventBundleNodeBase<?, ?, ?>, Stream<EventBundleNodeBase<?, ?, ?>>>() {
|
||||
@Override
|
||||
|
@ -68,22 +68,25 @@ final public class EventStripeNode extends EventBundleNodeBase<EventStripe, Even
|
||||
super(chart, eventStripe, parentNode);
|
||||
|
||||
setMinHeight(48);
|
||||
|
||||
//setup description label
|
||||
eventTypeImageView.setImage(getEventType().getFXImage());
|
||||
descrLabel.setTextOverrun(OverrunStyle.CENTER_ELLIPSIS);
|
||||
descrLabel.setGraphic(eventTypeImageView);
|
||||
|
||||
descrLabel.setPrefWidth(USE_COMPUTED_SIZE);
|
||||
setAlignment(subNodePane, Pos.BOTTOM_LEFT);
|
||||
|
||||
for (EventCluster cluster : eventStripe.getClusters()) {
|
||||
EventClusterNode clusterNode = new EventClusterNode(chart, cluster, this);
|
||||
subNodes.add(clusterNode);
|
||||
subNodePane.getChildren().addAll(clusterNode);
|
||||
subNodes.add(createChildNode(cluster));
|
||||
}
|
||||
|
||||
getChildren().addAll(new VBox(infoHBox, subNodePane));
|
||||
}
|
||||
|
||||
@Override
|
||||
EventClusterNode createChildNode(EventCluster cluster) {
|
||||
return new EventClusterNode(chart, cluster, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
void showHoverControls(final boolean showControls) {
|
||||
super.showHoverControls(showControls);
|
||||
@ -121,7 +124,7 @@ final public class EventStripeNode extends EventBundleNodeBase<EventStripe, Even
|
||||
|
||||
@Override
|
||||
void setDescriptionVisibiltiyImpl(DescriptionVisibility descrVis) {
|
||||
final int size = getEventStripe().getEventIDs().size();
|
||||
final int size = getEventStripe().getCount();
|
||||
|
||||
switch (descrVis) {
|
||||
case HIDDEN:
|
||||
|
@ -37,13 +37,15 @@ class EventDescriptionTreeItem extends NavTreeItem {
|
||||
*/
|
||||
private final Map<String, EventDescriptionTreeItem> childMap = new HashMap<>();
|
||||
private final EventBundle<?> bundle;
|
||||
private Comparator<TreeItem<EventBundle<?>>> comparator = TreeComparator.Description;
|
||||
|
||||
public EventBundle<?> getEventBundle() {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
EventDescriptionTreeItem(EventBundle<?> g) {
|
||||
EventDescriptionTreeItem(EventBundle<?> g, Comparator<TreeItem<EventBundle<?>>> comp) {
|
||||
bundle = g;
|
||||
comparator = comp;
|
||||
setValue(g);
|
||||
}
|
||||
|
||||
@ -56,10 +58,11 @@ class EventDescriptionTreeItem extends NavTreeItem {
|
||||
public void insert(Deque<EventBundle<?>> path) {
|
||||
EventBundle<?> head = path.removeFirst();
|
||||
EventDescriptionTreeItem treeItem = childMap.computeIfAbsent(head.getDescription(), description -> {
|
||||
EventDescriptionTreeItem newTreeItem = new EventDescriptionTreeItem(head);
|
||||
EventDescriptionTreeItem newTreeItem = new EventDescriptionTreeItem(head, comparator);
|
||||
newTreeItem.setExpanded(true);
|
||||
childMap.put(description, newTreeItem);
|
||||
getChildren().add(newTreeItem);
|
||||
resort(comparator, false);
|
||||
return newTreeItem;
|
||||
});
|
||||
|
||||
@ -81,8 +84,12 @@ class EventDescriptionTreeItem extends NavTreeItem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resort(Comparator<TreeItem<EventBundle<?>>> comp) {
|
||||
void resort(Comparator<TreeItem<EventBundle<?>>> comp, Boolean recursive) {
|
||||
this.comparator = comp;
|
||||
FXCollections.sort(getChildren(), comp);
|
||||
if (recursive) {
|
||||
childMap.values().forEach(ti -> ti.resort(comp, true));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,10 +34,11 @@ class EventTypeTreeItem extends NavTreeItem {
|
||||
*/
|
||||
private final Map<String, EventDescriptionTreeItem> childMap = new HashMap<>();
|
||||
|
||||
private final Comparator<TreeItem<EventBundle<?>>> comparator = TreeComparator.Description;
|
||||
private Comparator<TreeItem<EventBundle<?>>> comparator = TreeComparator.Description;
|
||||
|
||||
EventTypeTreeItem(EventBundle<?> g) {
|
||||
EventTypeTreeItem(EventBundle<?> g, Comparator<TreeItem<EventBundle<?>>> comp) {
|
||||
setValue(g);
|
||||
comparator = comp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,11 +50,11 @@ class EventTypeTreeItem extends NavTreeItem {
|
||||
public void insert(Deque<EventBundle<?>> path) {
|
||||
EventBundle<?> head = path.removeFirst();
|
||||
EventDescriptionTreeItem treeItem = childMap.computeIfAbsent(head.getDescription(), description -> {
|
||||
EventDescriptionTreeItem newTreeItem = new EventDescriptionTreeItem(head);
|
||||
EventDescriptionTreeItem newTreeItem = new EventDescriptionTreeItem(head, comparator);
|
||||
newTreeItem.setExpanded(true);
|
||||
childMap.put(head.getDescription(), newTreeItem);
|
||||
getChildren().add(newTreeItem);
|
||||
|
||||
resort(comparator, false);
|
||||
return newTreeItem;
|
||||
});
|
||||
|
||||
@ -63,16 +64,15 @@ class EventTypeTreeItem extends NavTreeItem {
|
||||
}
|
||||
|
||||
void remove(Deque<EventBundle<?>> path) {
|
||||
|
||||
EventBundle<?> head = path.removeFirst();
|
||||
EventDescriptionTreeItem descTreeItem = childMap.get(head.getDescription());
|
||||
if (descTreeItem != null) {
|
||||
if (path.isEmpty() == false) {
|
||||
descTreeItem.remove(path);
|
||||
} else if (descTreeItem.getChildren().isEmpty()) {
|
||||
}
|
||||
if (descTreeItem.getChildren().isEmpty()) {
|
||||
childMap.remove(head.getDescription());
|
||||
getChildren().remove(descTreeItem);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,8 +92,11 @@ class EventTypeTreeItem extends NavTreeItem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resort(Comparator<TreeItem<EventBundle<?>>> comp
|
||||
) {
|
||||
void resort(Comparator<TreeItem<EventBundle<?>>> comp, Boolean recursive) {
|
||||
this.comparator = comp;
|
||||
FXCollections.sort(getChildren(), comp);
|
||||
if (recursive) {
|
||||
childMap.values().forEach(ti -> ti.resort(comp, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,6 @@ final public class EventsTree extends BorderPane {
|
||||
getRoot().remove(bundle);
|
||||
}
|
||||
}
|
||||
getRoot().resort(sortByBox.getSelectionModel().getSelectedItem());
|
||||
});
|
||||
|
||||
setRoot();
|
||||
@ -114,11 +113,10 @@ final public class EventsTree extends BorderPane {
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private void setRoot() {
|
||||
RootItem root = new RootItem();
|
||||
RootItem root = new RootItem(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
|
||||
for (EventBundle<?> bundle : detailViewPane.getEventStripes()) {
|
||||
root.insert(bundle);
|
||||
}
|
||||
root.resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
|
||||
eventsTree.setRoot(root);
|
||||
|
||||
}
|
||||
@ -131,7 +129,7 @@ final public class EventsTree extends BorderPane {
|
||||
sortByBox.getItems().setAll(Arrays.asList(TreeComparator.Description, TreeComparator.Count));
|
||||
sortByBox.getSelectionModel().select(TreeComparator.Description);
|
||||
sortByBox.getSelectionModel().selectedItemProperty().addListener((Observable o) -> {
|
||||
getRoot().resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
|
||||
getRoot().resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()), true);
|
||||
});
|
||||
eventsTree.setShowRoot(false);
|
||||
|
||||
|
@ -32,9 +32,8 @@ abstract class NavTreeItem extends TreeItem<EventBundle<?>> {
|
||||
|
||||
abstract long getCount();
|
||||
|
||||
abstract void resort(Comparator<TreeItem<EventBundle<?>>> comp);
|
||||
abstract void resort(Comparator<TreeItem<EventBundle<?>>> comp, Boolean recursive);
|
||||
|
||||
abstract NavTreeItem findTreeItemForEvent(EventBundle<?> t);
|
||||
|
||||
|
||||
}
|
||||
|
@ -42,9 +42,10 @@ class RootItem extends NavTreeItem {
|
||||
/**
|
||||
* the comparator if any used to sort the children of this item
|
||||
*/
|
||||
// private TreeNodeComparators comp;
|
||||
RootItem() {
|
||||
private Comparator<TreeItem<EventBundle<?>>> comparator = TreeComparator.Type.reversed();
|
||||
|
||||
RootItem(Comparator<TreeItem<EventBundle<?>>> comp) {
|
||||
comp = comp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -62,12 +63,13 @@ class RootItem extends NavTreeItem {
|
||||
|
||||
EventTypeTreeItem treeItem = childMap.computeIfAbsent(bundle.getEventType().getBaseType(),
|
||||
baseType -> {
|
||||
EventTypeTreeItem newTreeItem = new EventTypeTreeItem(bundle);
|
||||
EventTypeTreeItem newTreeItem = new EventTypeTreeItem(bundle, comparator);
|
||||
newTreeItem.setExpanded(true);
|
||||
getChildren().add(newTreeItem);
|
||||
return newTreeItem;
|
||||
});
|
||||
treeItem.insert(getTreePath(bundle));
|
||||
|
||||
}
|
||||
|
||||
void remove(EventBundle<?> bundle) {
|
||||
@ -96,8 +98,9 @@ class RootItem extends NavTreeItem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resort(Comparator<TreeItem<EventBundle<?>>> comp) {
|
||||
childMap.values().forEach(ti -> ti.resort(comp));
|
||||
void resort(Comparator<TreeItem<EventBundle<?>>> comp, Boolean recursive) {
|
||||
comparator = comp;
|
||||
childMap.values().forEach(ti -> ti.resort(comp, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
BIN
docs/doxygen-user/images/autoPurge.PNG
Executable file
After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@ -55,7 +55,7 @@ The following steps will configure Solr to run using an account that will have a
|
||||
<br><br>
|
||||
\image html javaproperties.PNG
|
||||
<br><br>
|
||||
A fully updated _serviceinstall.bat_ is shown below, with the changes marked in yellow.
|
||||
A portion of an updated _serviceinstall.bat_ is shown below, with the changes marked in yellow.
|
||||
<br><br>
|
||||
\image html updatedServiceInstall.PNG
|
||||
<br><br>
|
||||
@ -75,7 +75,11 @@ The added part is highlighted in yellow below. Ensure that it is inside the <i>\
|
||||
<br><br>
|
||||
\image html dataDir.PNG
|
||||
<br><br>
|
||||
7. Edit <i>"C:\Bitnami\solr-4.10.3-0\apache-solr\resources/log4j.properties"</i> to configure Solr log settings:
|
||||
7. Edit the file <i>"C:\Bitnami\solr-4.10.3-0\apache-solr\solr\zoo.cfg"</i> to add the lines <i>autopurge.snapRetainCount=3</i> and <i>autopurge.purgeInterval=1</i> as shown in the screenshot below.
|
||||
<br><br>
|
||||
\image html autoPurge.PNG
|
||||
<br><br>
|
||||
8. Edit <i>"C:\Bitnami\solr-4.10.3-0\apache-solr\resources/log4j.properties"</i> to configure Solr log settings:
|
||||
- Increase the log rotation size threshold (_log4j\.appender\.file\.MaxFileSize_) from 4MB to 100MB.
|
||||
- Remove the _CONSOLE_ appender from the _log4j\.rootLogger_ line.
|
||||
<br><br>
|
||||
@ -85,9 +89,9 @@ The log file should end up looking like this (modified lines are highlighted in
|
||||
<br><br>
|
||||
\image html log4j.PNG
|
||||
<br><br>
|
||||
8. From an Autopsy installation, copy the folder <i>"C:\Program Files\Autopsy-4.0\autopsy\solr\solr\configsets"</i> to <i>"C:\Bitnami\solr-4.10.3-0\apache-solr\solr"</i>.
|
||||
9. From an Autopsy installation, copy the folder <i>"C:\Program Files\Autopsy-4.0\autopsy\solr\solr\lib"</i> to <i>"C:\Bitnami\solr-4.10.3-0\apache-solr\solr"</i>.
|
||||
10. Start a Windows command prompt as administrator by pressing _Start_, typing _command_, right clicking on _Command Prompt_, and clicking on _Run as administrator_. Then run the following command to install the _solrJetty_ service:
|
||||
9. From an Autopsy installation, copy the folder <i>"C:\Program Files\Autopsy-4.0\autopsy\solr\solr\configsets"</i> to <i>"C:\Bitnami\solr-4.10.3-0\apache-solr\solr"</i>.
|
||||
10. From an Autopsy installation, copy the folder <i>"C:\Program Files\Autopsy-4.0\autopsy\solr\solr\lib"</i> to <i>"C:\Bitnami\solr-4.10.3-0\apache-solr\solr"</i>.
|
||||
11. Start a Windows command prompt as administrator by pressing _Start_, typing _command_, right clicking on _Command Prompt_, and clicking on _Run as administrator_. Then run the following command to install the _solrJetty_ service:
|
||||
<br><br>
|
||||
<i>cmd /c C:\\Bitnami\\solr-4.10.3-0\\apache-solr\\scripts\\serviceinstall.bat INSTALL</i>
|
||||
<br><br>
|
||||
@ -95,13 +99,13 @@ The log file should end up looking like this (modified lines are highlighted in
|
||||
<br><br>
|
||||
\image html solrinstall1.PNG
|
||||
<br><br>
|
||||
11. Press _Start_, type _services.msc_, and press _Enter_. Find _solrJetty_. If the service is running, press _Stop the service_, then double click it, and switch to the _Log On_ tab to change the logon credentials to a user who will have access to read and write the primary shared drive. If the machine is on a domain, the Account Name will be in the form of _DOMAINNAME\\username_ as shown in the example below. Note that in the screenshot below, the domain name is _DOMAIN_ and the user name is _username_. These are just examples, not real values.
|
||||
12. Press _Start_, type _services.msc_, and press _Enter_. Find _solrJetty_. If the service is running, press _Stop the service_, then double click it, and switch to the _Log On_ tab to change the logon credentials to a user who will have access to read and write the primary shared drive. If the machine is on a domain, the Account Name will be in the form of _DOMAINNAME\\username_ as shown in the example below. Note that in the screenshot below, the domain name is _DOMAIN_ and the user name is _username_. These are just examples, not real values.
|
||||
<br><br>
|
||||
\image html solrinstall2.PNG
|
||||
<br>
|
||||
If the machine is on a domain, **make sure** to select the domain with the mouse by going to the _Log On_ tab, clicking _Browse_, then clicking _Locations_ and selecting the domain of interest. Then enter the user name desired and press _Check Names_. When that completes, press _OK_, type in the password once for each box and press _OK_. You may see "The user has been granted the log on as a service right."
|
||||
|
||||
12. You should be able to see the Solr service in a web browser via the URL <i>http://localhost:8983/solr/#/</i> as shown in the screenshot below.
|
||||
13. You should be able to see the Solr service in a web browser via the URL <i>http://localhost:8983/solr/#/</i> as shown in the screenshot below.
|
||||
<br><br>
|
||||
\image html solrinstall3.PNG
|
||||
<br><br>
|
||||
|