diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ContentTypePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ContentTypePanel.java index 0a8eb2c303..07664d9401 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ContentTypePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ContentTypePanel.java @@ -68,9 +68,5 @@ abstract class ContentTypePanel extends JPanel { */ abstract public void select(); - @Override - abstract public void addPropertyChangeListener(PropertyChangeListener pcl); - @Override - abstract public void removePropertyChangeListener(PropertyChangeListener pcl); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index f7bc809a8c..8c57567668 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -31,8 +31,8 @@ import javax.swing.event.DocumentListener; * ImageTypePanel for adding an image file such as .img, .E0x, .00x, etc. */ public class ImageFilePanel extends ContentTypePanel implements DocumentListener { - private static ImageFilePanel instance; - private PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private static ImageFilePanel instance = null; + private PropertyChangeSupport pcs = null; private JFileChooser fc = new JFileChooser(); /** @@ -47,19 +47,27 @@ public class ImageFilePanel extends ContentTypePanel implements DocumentListener fc.addChoosableFileFilter(AddImageVisualPanel1.rawFilter); fc.addChoosableFileFilter(AddImageVisualPanel1.encaseFilter); fc.setFileFilter(AddImageVisualPanel1.allFilter); - pathTextField.getDocument().addDocumentListener(this); } /** * Returns the default instance of a ImageFilePanel. */ - public static ImageFilePanel getDefault() { + public static synchronized ImageFilePanel getDefault() { if (instance == null) { instance = new ImageFilePanel(); + instance.postInit(); } return instance; } + //post-constructor initialization to properly initialize listener support + //without leaking references of uninitialized objects + private void postInit() { + pathTextField.getDocument().addDocumentListener(this); + } + + + /** * 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 @@ -217,12 +225,20 @@ public class ImageFilePanel extends ContentTypePanel implements DocumentListener } @Override - public void addPropertyChangeListener(PropertyChangeListener pcl) { + public synchronized void addPropertyChangeListener(PropertyChangeListener pcl) { + super.addPropertyChangeListener(pcl); + + if (pcs == null) { + pcs = new PropertyChangeSupport(this); + } + pcs.addPropertyChangeListener(pcl); } @Override public void removePropertyChangeListener(PropertyChangeListener pcl) { + super.removePropertyChangeListener(pcl); + pcs.removePropertyChangeListener(pcl); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java index 1ac9e5ed5c..9c9c3f5fe6 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java @@ -45,7 +45,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil; */ public class LocalDiskPanel extends ContentTypePanel { private static LocalDiskPanel instance; - private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private PropertyChangeSupport pcs = null; private List disks = new ArrayList(); private LocalDiskModel model; private boolean enableNext = false; @@ -61,13 +61,14 @@ public class LocalDiskPanel extends ContentTypePanel { /** * Get the default instance of this panel. */ - public static LocalDiskPanel getDefault() { + public static synchronized LocalDiskPanel getDefault() { if (instance == null) { instance = new LocalDiskPanel(); } return instance; } + private void customInit() { model = new LocalDiskModel(); diskComboBox.setModel(model); @@ -189,12 +190,20 @@ public class LocalDiskPanel extends ContentTypePanel { } @Override - public void addPropertyChangeListener(PropertyChangeListener pcl) { + public synchronized void addPropertyChangeListener(PropertyChangeListener pcl) { + super.addPropertyChangeListener(pcl); + + if (pcs == null) { + pcs = new PropertyChangeSupport(this); + } + pcs.addPropertyChangeListener(pcl); } @Override public void removePropertyChangeListener(PropertyChangeListener pcl) { + super.removePropertyChangeListener(pcl); + pcs.removePropertyChangeListener(pcl); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java index d15f103acd..2357151d2b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java @@ -30,7 +30,7 @@ import javax.swing.JFileChooser; */ public class LocalFilesPanel extends ContentTypePanel { - private PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private PropertyChangeSupport pcs = null; private Set currentFiles = new TreeSet(); //keep currents in a set to disallow duplicates per add private boolean enableNext = false; private static LocalFilesPanel instance; @@ -55,9 +55,8 @@ public class LocalFilesPanel extends ContentTypePanel { localFileChooser.setMultiSelectionEnabled(true); selectedPaths.setText(""); - } - + @Override public String getContentPaths() { //TODO consider interface change to return list of paths instead @@ -102,16 +101,25 @@ public class LocalFilesPanel extends ContentTypePanel { pcs.firePropertyChange(AddImageVisualPanel1.EVENT.UPDATE_UI.toString(), false, true); } - @Override - public void addPropertyChangeListener(PropertyChangeListener pcl) { + @Override + public synchronized void addPropertyChangeListener(PropertyChangeListener pcl) { + super.addPropertyChangeListener(pcl); + + if (pcs == null) { + pcs = new PropertyChangeSupport(this); + } + pcs.addPropertyChangeListener(pcl); } @Override public void removePropertyChangeListener(PropertyChangeListener pcl) { + super.removePropertyChangeListener(pcl); + pcs.removePropertyChangeListener(pcl); } + @Override public String toString() { return "Logical Files"; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java index a251950ba6..cb2c95799d 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java @@ -41,6 +41,7 @@ import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.SolrInputDocument; import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.keywordsearch.Server.SolrServerNoPortException; @@ -52,10 +53,10 @@ import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.File; import org.sleuthkit.datamodel.FsContent; -import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LocalFile; import org.sleuthkit.datamodel.ReadContentInputStream; +import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** @@ -184,6 +185,12 @@ public class Ingester { private class GetContentFieldsV extends ContentVisitor.Default> { + private SleuthkitCase curCase = null; + + GetContentFieldsV() { + curCase = Case.getCurrentCase().getSleuthkitCase(); + } + @Override protected Map defaultVisit(Content cntnt) { return new HashMap(); @@ -217,11 +224,7 @@ public class Ingester { @Override public Map visit(LocalFile lf) { - final Map params = new HashMap(); - params.put(Server.Schema.ID.toString(), Long.toString(lf.getId())); - params.put(Server.Schema.FILE_NAME.toString(), lf.getName()); - params.put(Server.Schema.IMAGE_ID.toString(), Long.toString(-1)); - return params; + return getCommonFields(lf); } private Map getCommonFsContentFields(Map params, FsContent fsContent) { @@ -235,15 +238,13 @@ public class Ingester { private Map getCommonFields(AbstractFile af) { Map params = new HashMap(); params.put(Server.Schema.ID.toString(), Long.toString(af.getId())); - long imageId = -1; + long dataSourceId = -1; try { - Image image = af.getImage(); - if (image != null) { - imageId = image.getId(); - } - params.put(Server.Schema.IMAGE_ID.toString(), Long.toString(imageId)); + dataSourceId = curCase.getFileDataSource(af); + params.put(Server.Schema.IMAGE_ID.toString(), Long.toString(dataSourceId)); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Could not get image id to properly index the file " + af.getId()); + logger.log(Level.SEVERE, "Could not get data source id to properly index the file " + af.getId()); + params.put(Server.Schema.IMAGE_ID.toString(), Long.toString(-1)); } params.put(Server.Schema.FILE_NAME.toString(), af.getName()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordQueryFilter.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordQueryFilter.java index a62b884ae8..1afbc464a9 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordQueryFilter.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordQueryFilter.java @@ -18,6 +18,10 @@ */ package org.sleuthkit.autopsy.keywordsearch; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + /** * * Filter to restrict query only specific files, chunks, images @@ -27,23 +31,23 @@ public class KeywordQueryFilter { public static enum FilterType { - FILE, CHUNK, IMAGE + FILE, CHUNK, DATA_SOURCE }; - private long[] idFilters; + private SetidFilters; private FilterType filterType; public KeywordQueryFilter(FilterType filterType, long id) { this.filterType = filterType; - this.idFilters = new long[1]; - this.idFilters[0] = id; + this.idFilters = new HashSet(); + this.idFilters.add(id); } - public KeywordQueryFilter(FilterType filterType, long[] ids) { + public KeywordQueryFilter(FilterType filterType, Setids) { this.filterType = filterType; this.idFilters = ids; } - public long[] getIdFilters() { + public Set getIdFilters() { return idFilters; } @@ -55,12 +59,14 @@ public class KeywordQueryFilter { public String toString() { StringBuilder sb = new StringBuilder(); String id = null; - for (int i = 0; i < idFilters.length; ++i) { + + Iteratorit = idFilters.iterator(); + for (int i = 0; it.hasNext(); ++i) { if (i > 0) { sb.append(" "); //OR } - long idVal = idFilters[i]; - if (filterType == FilterType.IMAGE) { + long idVal = it.next(); + if (filterType == FilterType.DATA_SOURCE) { id = Server.Schema.IMAGE_ID.toString(); } else { id = Server.Schema.ID.toString(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index c6d3538fc8..001ae393b0 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -44,6 +44,7 @@ import org.netbeans.api.progress.aggregate.AggregateProgressFactory; import org.netbeans.api.progress.aggregate.AggregateProgressHandle; import org.netbeans.api.progress.aggregate.ProgressContributor; import org.openide.util.Cancellable; +import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.StopWatch; @@ -60,6 +61,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.SleuthkitCase; @@ -115,7 +117,7 @@ public final class KeywordSearchIngestModule extends IngestModuleAbstractFile { private Map> currentResults; //only search images from current ingest, not images previously ingested/indexed //accessed read-only by searcher thread - private Set curImageIds; + private Set curDataSourceIds; private static final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); //use fairness policy private static final Lock searcherLock = rwLock.writeLock(); private volatile int messageID = 0; @@ -128,6 +130,7 @@ public final class KeywordSearchIngestModule extends IngestModuleAbstractFile { private boolean initialized = false; private KeywordSearchConfigurationPanel panel; private Tika tikaFormatDetector; + private enum IngestStatus { @@ -160,12 +163,10 @@ public final class KeywordSearchIngestModule extends IngestModuleAbstractFile { return ProcessResult.OK; } try { - //add image id of the file to the set, keeping track of images being ingested - final Image fileImage = abstractFile.getImage(); - if (fileImage != null) { - //not all Content objects have an image associated (e.g. LocalFiles) - curImageIds.add(fileImage.getId()); - } + //add data source id of the file to the set, keeping track of images being ingested + final long fileSourceId = caseHandle.getFileDataSource(abstractFile); + curDataSourceIds.add(fileSourceId); + } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error getting image id of file processed by keyword search: " + abstractFile.getName(), ex); } @@ -288,7 +289,7 @@ public final class KeywordSearchIngestModule extends IngestModuleAbstractFile { private void cleanup() { ingestStatus.clear(); currentResults.clear(); - curImageIds.clear(); + curDataSourceIds.clear(); currentSearcher = null; //finalSearcher = null; //do not collect, might be finalizing @@ -399,7 +400,7 @@ public final class KeywordSearchIngestModule extends IngestModuleAbstractFile { //keeps track of all results per run not to repeat reporting the same hits currentResults = new HashMap>(); - curImageIds = new HashSet(); + curDataSourceIds = new HashSet(); indexer = new Indexer(); @@ -930,15 +931,10 @@ public final class KeywordSearchIngestModule extends IngestModuleAbstractFile { del = new TermComponentQuery(keywordQuery); } - //limit search to currently ingested images - final long imageIds[] = new long[curImageIds.size()]; - final Iterator it = curImageIds.iterator(); - for (int imageI = 0; it.hasNext(); ++imageI) { - imageIds[imageI] = it.next(); - } + //limit search to currently ingested data sources //set up a filter with 1 or more image ids OR'ed - final KeywordQueryFilter imageFilter = new KeywordQueryFilter(KeywordQueryFilter.FilterType.IMAGE, imageIds); - del.addFilter(imageFilter); + final KeywordQueryFilter dataSourceFilter = new KeywordQueryFilter(KeywordQueryFilter.FilterType.DATA_SOURCE, curDataSourceIds); + del.addFilter(dataSourceFilter); Map> queryResult = null; diff --git a/NEWS.txt b/NEWS.txt index 8d27e46e62..28a9453ec0 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -3,6 +3,7 @@ New features: - Logical files and folders support - New file views in directory tree to view: deleted, executable, archive files and files by size +- ext4 and yaffs2 support (via TSK 4.1.0) Improvements: - Improvements to tagging of files and keyword search results @@ -14,6 +15,7 @@ Bugfixes: - fixed result viewer for "File Search by MD5 Hash" - fix Solr, Timeline and RecentActivity issues with java 7.0.21 - Views->Recent Files showing inconsistent results when clicked many times +- reduced memory usage in Timeline ---------------- VERSION 3.0.5 -------------- diff --git a/Timeline/src/org/sleuthkit/autopsy/timeline/Timeline.java b/Timeline/src/org/sleuthkit/autopsy/timeline/Timeline.java index 1c2d29400e..74aafa2b59 100644 --- a/Timeline/src/org/sleuthkit/autopsy/timeline/Timeline.java +++ b/Timeline/src/org/sleuthkit/autopsy/timeline/Timeline.java @@ -77,13 +77,13 @@ import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; import org.openide.modules.InstalledFileLocator; import org.openide.modules.ModuleInstall; -import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.Presenter; +import org.openide.util.lookup.Lookups; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.corecomponents.DataContentPanel; @@ -98,14 +98,20 @@ import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.coreutils.ExecUtil; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline") -@ActionRegistration(displayName = "#CTL_MakeTimeline", lazy=false) +@ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false) @ActionReferences(value = { @ActionReference(path = "Menu/Tools", position = 100)}) @NbBundle.Messages(value = "CTL_TimelineView=Generate Timeline") +/** + * The Timeline Action entry point. Collects data and pushes data to javafx + * widgets + * + */ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, PropertyChangeListener { private static final Logger logger = Logger.getLogger(Timeline.class.getName()); @@ -116,14 +122,14 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, private HBox fxHBoxCharts; //Holds the navigation buttons in horiztonal fashion. private VBox fxVBox; //Holds the JavaFX Elements in vertical fashion. private JFXPanel fxPanelCharts; //FX panel to hold the group - private BarChart fxChartEvents; //Yearly/Monthly events - Bar chart + private BarChart fxChartEvents; //Yearly/Monthly events - Bar chart private ScrollPane fxScrollEvents; //Scroll Panes for dealing with oversized an oversized chart private static final int FRAME_HEIGHT = 700; //Sizing constants private static final int FRAME_WIDTH = 1200; private Button fxZoomOutButton; //Navigation buttons private ComboBox fxDropdownSelectYears; //Dropdown box for selecting years. Useful when the charts' scale means some years are unclickable, despite having events. - private final Stack> fxStackPrevCharts = new Stack>(); //Stack for storing drill-up information. - private BarChart fxChartTopLevel; //the topmost chart, used for resetting to default view. + private final Stack> fxStackPrevCharts = new Stack>(); //Stack for storing drill-up information. + private BarChart fxChartTopLevel; //the topmost chart, used for resetting to default view. private DataResultPanel dataResultPanel; private DataContentPanel dataContentPanel; private ProgressHandle progress; @@ -146,7 +152,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, if (coreInstaller != null) { fxInited = coreInstaller.isJavaFxInited(); } - + } //Swing components and JavafX components don't play super well together @@ -272,7 +278,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, fxZoomOutButton.setOnAction(new EventHandler() { @Override public void handle(ActionEvent e) { - BarChart bc; + BarChart bc; if (fxStackPrevCharts.size() == 0) { bc = fxChartTopLevel; } else { @@ -339,7 +345,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, * @param allYears The list of years that have barData from the mactime file * @return BarChart scaled to the year level */ - private BarChart createYearChartWithDrill(final List allYears) { + private BarChart createYearChartWithDrill(final List allYears) { final CategoryAxis xAxis = new CategoryAxis(); //Axes are very specific types. Categorys are strings. final NumberAxis yAxis = new NumberAxis(); final Label l = new Label(""); @@ -354,11 +360,11 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, BarChart.Series se = new BarChart.Series(); if (allYears != null) { for (final YearEpoch ye : allYears) { - se.getData().add(new BarChart.Data(String.valueOf(ye.year), ye.getNumFiles())); + se.getData().add(new BarChart.Data(String.valueOf(ye.year), ye.getNumFiles())); } } bcData.add(se); - + //Note: // BarChart.Data wraps the Java Nodes class. BUT, until a BarChart.Data gets added to an actual series, it's node is null, and you can perform no operations on it. @@ -367,7 +373,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, // But it is for this reason that the chart generating functions have two forloops. I do not believe they can be condensed into a single loop due to the nodes being null until // an undetermined point in time. BarChart bc = new BarChart(xAxis, yAxis, bcData); - for (final BarChart.Data barData : bc.getData().get(0).getData()) { //.get(0) refers to the BarChart.Series class to work on. There is only one series in this graph, so get(0) is safe. + for (final BarChart.Data barData : bc.getData().get(0).getData()) { //.get(0) refers to the BarChart.Series class to work on. There is only one series in this graph, so get(0) is safe. barData.getNode().setScaleX(.5); final javafx.scene.Node barNode = barData.getNode(); @@ -385,7 +391,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, Platform.runLater(new Runnable() { @Override public void run() { - BarChart b = + BarChart b = createMonthsWithDrill(findYear(allYears, Integer.valueOf(barData.getXValue()))); fxChartEvents = b; fxScrollEvents.setContent(fxChartEvents); @@ -408,7 +414,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, * Displays a chart with events from one year only, separated into 1-month chunks. * Always 12 per year, empty months are represented by no bar. */ - private BarChart createMonthsWithDrill(final YearEpoch ye) { + private BarChart createMonthsWithDrill(final YearEpoch ye) { final CategoryAxis xAxis = new CategoryAxis(); final NumberAxis yAxis = new NumberAxis(); @@ -427,7 +433,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, final BarChart bc = new BarChart(xAxis, yAxis, bcData); for (int i = 0; i < 12; i++) { - for (final BarChart.Data barData : bc.getData().get(0).getData()) { + for (final BarChart.Data barData : bc.getData().get(0).getData()) { //Note: // All the charts of this package have a problem where when the chart gets below a certain pixel ratio, the barData stops drawing. The axes and the labels remain, // But the actual chart barData is invisible, unclickable, and unrendered. To partially compensate for that, barData.getNode() can be manually scaled up to increase visibility. @@ -473,22 +479,21 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, * Displays a chart with events from one month only. * Up to 31 days per month, as low as 28 as determined by the specific MonthEpoch */ - private BarChart createEventsByMonth(final MonthEpoch me, final YearEpoch ye) { + private BarChart createEventsByMonth(final MonthEpoch me, final YearEpoch ye) { final CategoryAxis xAxis = new CategoryAxis(); final NumberAxis yAxis = new NumberAxis(); xAxis.setLabel("Day of Month"); yAxis.setLabel("Number of Events"); - ObservableList> bcData - = makeObservableListByMonthAllDays(me, ye.getYear()); - BarChart.Series series = new BarChart.Series(bcData); + ObservableList> bcData = makeObservableListByMonthAllDays(me, ye.getYear()); + BarChart.Series series = new BarChart.Series(bcData); series.setName(me.getMonthName() + " " + ye.getYear()); - ObservableList> ol = + ObservableList> ol = FXCollections.>observableArrayList(series); final BarChart bc = new BarChart(xAxis, yAxis, ol); - for (final BarChart.Data barData : bc.getData().get(0).getData()) { + for (final BarChart.Data barData : bc.getData().get(0).getData()) { //data.getNode().setScaleX(2); final javafx.scene.Node barNode = barData.getNode(); @@ -503,9 +508,18 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, @Override public void handle(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + //reset the view and free the current nodes before loading new ones + final FileRootNode d = new FileRootNode("Empty Root", new ArrayList()); + dataResultPanel.setNode(d); + dataResultPanel.setPath("Loading..."); + } + }); final int day = (Integer.valueOf((barData.getXValue()).split("-")[1])); final DayEpoch de = myme.getDay(day); - final List afs; + final List afs; if (de != null) { afs = de.getEvents(); } else { @@ -516,7 +530,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - final FileRootNode d = new FileRootNode("Test Root", afs); + final FileRootNode d = new FileRootNode("Root", afs); dataResultPanel.setNode(d); //set result viewer title path with the current date String dateString = ye.getYear() + "-" + (1 + me.getMonthInt()) + "-" + +de.dayNum; @@ -533,13 +547,13 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, return bc; } - private static ObservableList> makeObservableListByMonthAllDays(final MonthEpoch me, int year) { - ObservableList> bcData = FXCollections.observableArrayList(); + private static ObservableList> makeObservableListByMonthAllDays(final MonthEpoch me, int year) { + ObservableList> bcData = FXCollections.observableArrayList(); int totalDays = me.getTotalNumDays(year); for (int i = 1; i <= totalDays; ++i) { DayEpoch day = me.getDay(i); int numFiles = day == null ? 0 : day.getNumFiles(); - BarChart.Data d = new BarChart.Data(me.month + 1 + "-" + i, numFiles); + BarChart.Data d = new BarChart.Data(me.month + 1 + "-" + i, numFiles); d.setExtraValue(me); bcData.add(d); } @@ -693,7 +707,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, return month; } - public void add(AbstractFile af, int month, int day) { + public void add(long fileId, int month, int day) { // see if this month is in the list MonthEpoch monthEpoch = null; for (MonthEpoch me : months) { @@ -709,7 +723,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, } // add the file the the MonthEpoch object - monthEpoch.add(af, day); + monthEpoch.add(fileId, day); } } @@ -752,7 +766,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, return de; } - public void add(AbstractFile af, int day) { + public void add(long fileId, int day) { DayEpoch dayEpoch = null; for (DayEpoch de : days) { if (de.getDayInt() == day) { @@ -766,7 +780,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, days.add(dayEpoch); } - dayEpoch.add(af); + dayEpoch.add(fileId); } /** @@ -786,7 +800,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, private class DayEpoch extends Epoch { - private List files = new ArrayList<>(); + private final List fileIds = new ArrayList<>(); int dayNum = 0; //Day of the month this Epoch represents, 1 indexed: 28=28. DayEpoch(int dayOfMonth) { @@ -799,40 +813,68 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, @Override public int getNumFiles() { - return files.size(); + return fileIds.size(); } - public void add(AbstractFile af) { - files.add(af); + public void add(long fileId) { + fileIds.add(fileId); } - List getEvents() { - return this.files; + List getEvents() { + return this.fileIds; } } // The node factories used to make lists of files to send to the result viewer - private class FileNodeChildFactory extends ChildFactory { + // using the lazy loading (rather than background) loading option to facilitate + // loading a huge number of nodes for the given day + private class FileNodeChildFactory extends Children.Keys { - List l; + private List fileIds; - FileNodeChildFactory(List l) { - this.l = l; + FileNodeChildFactory(List fileIds) { + super(true); + this.fileIds = fileIds; } @Override - protected boolean createKeys(List list) { - list.addAll(l); - return true; + protected void addNotify() { + super.addNotify(); + setKeys(fileIds); } @Override - protected Node createNodeForKey(AbstractFile file) { + protected void removeNotify() { + super.removeNotify(); + setKeys(new ArrayList()); + } + + @Override + protected Node[] createNodes(Long t) { + return new Node[]{createNodeForKey(t)}; + } + + // @Override + // protected boolean createKeys(List list) { + // list.addAll(fileIds); + // return true; + // } + //@Override + protected Node createNodeForKey(Long fileId) { + AbstractFile af = null; + try { + af = skCase.getAbstractFileById(fileId); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting file by id and creating a node in Timeline: " + fileId, ex); + //no node will be shown for this object + return null; + } + Node wrapped; - if (file.isDir()) { - wrapped = new DirectoryNode(file, false); + if (af.isDir()) { + wrapped = new DirectoryNode(af, false); } else { - wrapped = new FileNode(file, false); + wrapped = new FileNode(af, false); } return new FilterNodeLeaf(wrapped); } @@ -840,8 +882,9 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, private class FileRootNode extends DisplayableItemNode { - FileRootNode(String NAME, List l) { - super(Children.create(new FileNodeChildFactory(l), true)); + FileRootNode(String NAME, List fileIds) { + //super(Children.create(new FileNodeChildFactory(fileIds), true)); + super(new FileNodeChildFactory(fileIds), Lookups.singleton(fileIds)); super.setName(NAME); super.setDisplayName(NAME); } @@ -887,16 +930,8 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, prevYear = year; } - // create and add the file - AbstractFile file; - try { - file = skCase.getAbstractFileById(ObjId); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Could not find a file with ID " + ObjId, ex); - continue; - } if (ye != null) { - ye.add(file, month, day); + ye.add(ObjId, month, day); } } @@ -923,11 +958,11 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, + java.io.File.separator + currentCase.getName() + "-" + datenotime + ".txt"; // Run query to get all files - String filesAndDirs = "name != '.' " + final String filesAndDirs = "name != '.' " + "AND name != '..'"; - List files = null; + List fileIds = null; try { - files = skCase.findAllFilesWhere(filesAndDirs); + fileIds = skCase.findAllFileIdsWhere(filesAndDirs); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error querying image files to make a body file: " + bodyFilePath, ex); return null; @@ -945,7 +980,8 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, BufferedWriter out = null; try { out = new BufferedWriter(fileWriter); - for (AbstractFile file : files) { + for (long fileId : fileIds) { + AbstractFile file = skCase.getAbstractFileById(fileId); // try { // MD5|name|inode|mode_as_string|ObjId|GID|size|atime|mtime|ctime|crtime if (file.getMd5Hash() != null) { @@ -985,6 +1021,10 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, out.write(Long.toString(file.getCrtime())); out.write("\n"); } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error querying file by id", ex); + return null; + } catch (IOException ex) { logger.log(Level.WARNING, "Error while trying to write data to the body file.", ex); return null; @@ -1030,8 +1070,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, } catch (IOException ioe) { logger.log(Level.SEVERE, "Could not create mactime file, encountered error ", ioe); return null; - } - finally { + } finally { if (writer != null) { try { writer.close(); @@ -1040,7 +1079,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, } } } - + return macfile; } diff --git a/docs/QuickStartGuide/index.html b/docs/QuickStartGuide/index.html index 8a6d9f9f03..46b762a4bc 100644 --- a/docs/QuickStartGuide/index.html +++ b/docs/QuickStartGuide/index.html @@ -11,7 +11,7 @@

Autopsy 3 Quick Start Guide

-

June 2012

+

June 2013

www.sleuthkit.org/autopsy/