diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/Bundle.properties b/Ingest/src/org/sleuthkit/autopsy/ingest/Bundle.properties index 35f0333705..0e6f76bb3e 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/Bundle.properties +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/Bundle.properties @@ -4,5 +4,7 @@ HINT_IngestTopComponent=Ingest window OpenIDE-Module-Name=Ingest IngestTopComponent.topLable.text=Image ingest services IngestTopComponent.startButton.text=Start -IngestTopComponent.refreshFreqLabel.text=Refresh frequency IngestTopComponent.messageFrame.title=Ingest Messages +IngestTopComponent.refreshFreqLabel.text=Refresh frequency +IngestTopComponent.refreshFrequencyLabel.text=Refresh Frequency +IngestTopComponent.ingestProgressLabel.text=File Ingest Progress diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestImageThread.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestImageThread.java new file mode 100644 index 0000000000..49b477af3f --- /dev/null +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestImageThread.java @@ -0,0 +1,117 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 Basis Technology Corp. + * Contact: carrier sleuthkit 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.ingest; + +//ingester worker for image queue +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.SwingWorker; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; +import org.openide.util.Cancellable; +import org.sleuthkit.datamodel.Image; + +/** + * worker for every ingest image service + * there is a separate instance per image / service pair + */ +public class IngestImageThread extends SwingWorker { + + private Logger logger = Logger.getLogger(IngestImageThread.class.getName()); + private ProgressHandle progress; + private Image image; + private IngestServiceImage service; + private IngestImageWorkerController controller; + private IngestManager manager; + + IngestImageThread(IngestManager manager, Image image, IngestServiceImage service) { + this.manager = manager; + this.image = image; + this.service = service; + } + + Image getImage() { + return image; + } + + IngestServiceImage getService() { + return service; + } + + @Override + protected Object doInBackground() throws Exception { + + logger.log(Level.INFO, "Starting background processing"); + + progress = ProgressHandleFactory.createHandle(service.getName() + " image id:" + image.getId(), new Cancellable() { + + @Override + public boolean cancel() { + return IngestImageThread.this.cancel(true); + } + }); + + progress.start(); + progress.switchToIndeterminate(); + + controller = new IngestImageWorkerController(this, progress); + + if (isCancelled()) { + return null; + } + try { + service.process(image, controller); + } catch (Exception e) { + logger.log(Level.INFO, "Exception in service: " + service.getName() + " image: " + image.getName(), e); + } + logger.log(Level.INFO, + "Done background processing"); + return null; + } + + @Override + protected void done() { + try { + super.get(); //block and get all exceptions thrown while doInBackground() + //notify services of completion + if (!this.isCancelled()) { + service.complete(); + } + } catch (CancellationException e) { + //task was cancelled + service.stop(); + + } catch (InterruptedException ex) { + service.stop(); + } catch (ExecutionException ex) { + service.stop(); + logger.log(Level.SEVERE, "Fatal error during image ingest from sevice: " + service.getName() + " image: " + image.getName(), ex); + } catch (Exception ex) { + service.stop(); + logger.log(Level.SEVERE, "Fatal error during image ingest in service: " + service.getName() + " image: " + image.getName(), ex); + } finally { + progress.finish(); + + //cleanup queues (worker and image/service) + manager.removeImageIngestWorker(this); + } + } +} diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestImageWorkerController.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestImageWorkerController.java new file mode 100644 index 0000000000..4e07285404 --- /dev/null +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestImageWorkerController.java @@ -0,0 +1,58 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 Basis Technology Corp. + * Contact: carrier sleuthkit 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.ingest; + +import org.netbeans.api.progress.ProgressHandle; + +/** + * passed to the service as a limited way to control this worker + * update progress bar, check if job is cancelled + */ +public class IngestImageWorkerController { + + private IngestImageThread worker; + private ProgressHandle progress; + + public IngestImageWorkerController(IngestImageThread worker, ProgressHandle progress) { + this.worker = worker; + this.progress = progress; + } + + public boolean isCancelled() { + return worker.isCancelled(); + } + + public void switchToDeterminate(int workUnits) { + if (progress != null) { + progress.switchToDeterminate(workUnits); + } + } + + public void switchToInDeterminate() { + if (progress != null) { + progress.switchToIndeterminate(); + } + } + + public void progress(int workUnits) { + if (progress != null) { + progress.progress(worker.getImage().getName(), workUnits); + } + } +} \ No newline at end of file diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestManager.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestManager.java index 6167ff6796..ee90d85a8f 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestManager.java +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestManager.java @@ -39,6 +39,7 @@ import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; import org.openide.util.Cancellable; import org.openide.util.Lookup; +import org.sleuthkit.autopsy.ingest.IngestMessage.MessageType; import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.Image; @@ -56,10 +57,13 @@ public class IngestManager { private IngestManagerStats stats; private int updateFrequency; //queues - private final Object queueLock = new Object(); private final ImageQueue imageQueue = new ImageQueue(); private final FsContentQueue fsContentQueue = new FsContentQueue(); - private IngestThread ingester; + private final Object queuesLock = new Object(); + //workers + private IngestFsContentThread fsContentIngester; + private List imageIngesters; + //services final Collection imageServices = enumerateImageServices(); final Collection fsContentServices = enumerateFsContentServices(); @@ -70,52 +74,146 @@ public class IngestManager { IngestManager(IngestTopComponent tc) { this.tc = tc; + imageIngesters = new ArrayList(); + //one time initialization of services - for (IngestServiceImage s : imageServices) { - s.init(this); - } + + //image services are now initialized per instance + //for (IngestServiceImage s : imageServices) { + // s.init(this); + //} + for (IngestServiceFsContent s : fsContentServices) { s.init(this); } } + /** + * Multiple image version of execute, enqueues multiple images and associated services at once + * @param services services to execute on every image + * @param images images to execute services on + */ + void execute(final Collection services, final Collection images) { + tc.enableStartButton(false); + SwingWorker queueWorker = new EnqueueWorker(services, images); + queueWorker.execute(); + + //logger.log(Level.INFO, "Queues: " + imageQueue.toString() + " " + fsContentQueue.toString()); + } + /** * IngestManager entry point, enqueues image to be processed. * Spawns background thread which enumerates all sorted files and executes chosen services per file in a pre-determined order. * Notifies services when work is complete or should be interrupted using complete() and stop() calls. * Does not block and can be called multiple times to enqueue more work to already running background process. + * @param services services to execute on the image + * @param image image to execute services on */ - void execute(final Collection services, final Collection images) { - - //queuing start - tc.enableStartButton(false); - SwingWorker queueWorker = new EnqueueWorker(services, images); - queueWorker.execute(); - - logger.log(Level.INFO, "Queues: " + imageQueue.toString() + " " + fsContentQueue.toString()); - - + void execute(final Collection services, final Image image) { + Collection images = new ArrayList(); + images.add(image); + execute(services, images); } + /** + * manage current workers + * if fsContent service is still running, do nothing and allow it to consume queue + * otherwise start /restart fsContent worker + * + * image workers run per (service,image). Check if one for the (service,image) is already running + * otherwise start/restart the worker + */ private void startAll() { - boolean start = false; - if (ingester == null) { - start = true; + //image ingesters + while (hasNextImage()) { + //dequeue + final QueueUnit qu = + this.getNextImage(); + //check if such (service,image) already running - } //if worker had completed, restart it in case data is still enqueued - else if (ingester.isDone() - && (hasNextFsContent() || hasNextImage())) { - logger.log(Level.INFO, "Restarting ingester thread."); - start = true; - } else { - logger.log(Level.INFO, "Ingester is still running"); + + synchronized (this) { + for (IngestServiceImage quService : qu.services) { + boolean alreadyRunning = false; + for (IngestImageThread worker : imageIngesters) { + if (!worker.getImage().equals(qu.content)) { + continue; //check next worker + } + //same image, check service (by name, not id, since different instances) + if (worker.getService().getName().equals(quService.getName())) { + alreadyRunning = true; + logger.log(Level.INFO, "Image Ingester <" + qu.content + ", " + quService.getName() + "> is already running"); + break; + } + } + //checked all workers + if (alreadyRunning == false) { + logger.log(Level.INFO, "Starting new image Ingester <" + qu.content + ", " + quService.getName() + ">"); + IngestImageThread newImageWorker = new IngestImageThread(this, qu.content, quService); + + imageIngesters.add(newImageWorker); + + //image services are now initialized per instance + quService.init(this); + newImageWorker.execute(); + } + } + } } - if (start) { - logger.log(Level.INFO, "Starting new ingester."); - ingester = new IngestThread(); + + //fsContent ingester + boolean startFsContentIngester = false; + if (hasNextFsContent()) { + if (fsContentIngester + == null) { + startFsContentIngester = true; + logger.log(Level.INFO, "Starting initial FsContent ingester"); + } //if worker had completed, restart it in case data is still enqueued + else if (fsContentIngester.isDone()) { + startFsContentIngester = true; + logger.log(Level.INFO, "Restarting fsContent ingester"); + } + } else { + logger.log(Level.INFO, "no new FsContent enqueued, no ingester needed"); + } + + if (startFsContentIngester) { + fsContentIngester = new IngestFsContentThread(); stats = new IngestManagerStats(); - ingester.execute(); + fsContentIngester.execute(); + } + } + + /** + * stop currently running threads if any (e.g. when changing a case) + */ + void stopAll() { + //empty queues + emptyFsContents(); + emptyImages(); + + //stop workers + + if (fsContentIngester != null) { + boolean cancelled = fsContentIngester.cancel(true); + if (!cancelled) { + logger.log(Level.WARNING, "Unable to cancel file ingest worker"); + } else { + fsContentIngester = null; + } + } + + List toStop = new ArrayList(); + synchronized (this) { + toStop.addAll(imageIngesters); + } + + for (IngestImageThread imageWorker : toStop) { + boolean cancelled = imageWorker.cancel(true); + if (!cancelled) { + logger.log(Level.WARNING, "Unable to cancel image ingest worker for service: " + imageWorker.getService().getName() + " img: " + imageWorker.getImage().getName()); + } } } @@ -153,6 +251,13 @@ public class IngestManager { */ public synchronized void postMessage(final IngestMessage message) { + if (stats != null) { + //record the error for stats, if stats are running + if (message.getMessageType() == MessageType.ERROR) { + stats.addError(message.getSource()); + } + } + SwingUtilities.invokeLater(new Runnable() { @Override @@ -177,24 +282,19 @@ public class IngestManager { } private void addImage(IngestServiceImage service, Image image) { - - synchronized (queueLock) { + synchronized (queuesLock) { imageQueue.enqueue(image, service); - //queueLock.notifyAll(); } - - } private void addFsContent(IngestServiceFsContent service, Image image) { Collection fsContents = new GetAllFilesContentVisitor().visit(image); - synchronized (queueLock) { + + synchronized (queuesLock) { for (FsContent fsContent : fsContents) { fsContentQueue.enqueue(fsContent, service); } - //queueLock.notifyAll(); } - //logger.log(Level.INFO, fsContentQueue.toString()); } /** @@ -204,16 +304,15 @@ public class IngestManager { */ private QueueUnit getNextFsContent() { QueueUnit ret = null; - synchronized (queueLock) { + synchronized (queuesLock) { ret = fsContentQueue.dequeue(); - } return ret; } private boolean hasNextFsContent() { boolean ret = false; - synchronized (queueLock) { + synchronized (queuesLock) { ret = fsContentQueue.hasNext(); } return ret; @@ -221,20 +320,20 @@ public class IngestManager { private int getNumFsContents() { int ret = 0; - synchronized (queueLock) { + synchronized (queuesLock) { ret = fsContentQueue.getCount(); } return ret; } private void emptyFsContents() { - synchronized (queueLock) { + synchronized (queuesLock) { fsContentQueue.empty(); } } private void emptyImages() { - synchronized (queueLock) { + synchronized (queuesLock) { imageQueue.empty(); } } @@ -246,7 +345,7 @@ public class IngestManager { */ private QueueUnit getNextImage() { QueueUnit ret = null; - synchronized (queueLock) { + synchronized (queuesLock) { ret = imageQueue.dequeue(); } return ret; @@ -254,7 +353,7 @@ public class IngestManager { private boolean hasNextImage() { boolean ret = false; - synchronized (queueLock) { + synchronized (queuesLock) { ret = imageQueue.hasNext(); } return ret; @@ -262,12 +361,40 @@ public class IngestManager { private int getNumImages() { int ret = 0; - synchronized (queueLock) { + synchronized (queuesLock) { ret = imageQueue.getCount(); } return ret; } + private void initMainProgress(final int maximum) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + tc.initProgress(maximum); + } + }); + } + + private void updateMainProgress(final int progress) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + tc.updateProgress(progress); + } + }); + } + + //image worker to remove itself when complete or interrupted + void removeImageIngestWorker(IngestImageThread worker) { + //remove worker + synchronized (this) { + imageIngesters.remove(worker); + } + } + //manages queue of pending FsContent and IngestServiceFsContent to use on that content //TODO in future content sort will be maintained based on priorities private class FsContentQueue { @@ -334,12 +461,12 @@ public class IngestManager { } @Override - public String toString() { + public synchronized String toString() { return "FsContentQueue, size: " + Integer.toString(fsContentUnits.size()); } } - //manages queue of pending Images and IngestServiceImage to use on that image +//manages queue of pending Images and IngestServiceImage to use on that image private class ImageQueue { List> imageUnits = new ArrayList>(); @@ -404,7 +531,7 @@ public class IngestManager { } @Override - public String toString() { + public synchronized String toString() { return "ImageQueue, size: " + Integer.toString(imageUnits.size()); } } @@ -430,11 +557,14 @@ public class IngestManager { } //merge services with the current collection of services per image - //this assumes that there is one singleton instance of each type of service + //this assumes singleton instances of every service type for correct merge + //in case of multiple instances, they need to be handled correctly after dequeue() final void addAll(Collection services) { this.services.addAll(services); } + //this assumes singleton instances of every service type for correct merge + //in case of multiple instances, they need to be handled correctly after dequeue() final void add(S service) { this.services.add(service); } @@ -457,25 +587,48 @@ public class IngestManager { @Override public String toString() { + final String EOL = System.getProperty("line.separator"); StringBuilder sb = new StringBuilder(); if (startTime != null) { - sb.append("Start time: ").append(dateFormatter.format(startTime)).append("\n"); + sb.append("Start time: ").append(dateFormatter.format(startTime)).append(EOL); } if (endTime != null) { - sb.append("End time: ").append(dateFormatter.format(endTime)).append("\n"); + sb.append("End time: ").append(dateFormatter.format(endTime)).append(EOL); } - sb.append("Total ingest time: ").append(getTotalTimeString()).append("\n"); - sb.append("Total errors: ").append(errorsTotal).append("\n"); + sb.append("Total ingest time: ").append(getTotalTimeString()).append(EOL); + sb.append("Total errors: ").append(errorsTotal).append(EOL); if (errorsTotal > 0) { sb.append("Errors per service:"); for (IngestServiceAbstract service : errors.keySet()) { final int errorsService = errors.get(service); - sb.append("\t").append(service.getName()).append(": ").append(errorsService).append("\n"); + sb.append("\t").append(service.getName()).append(": ").append(errorsService).append(EOL); } } return sb.toString(); } + public String toHtmlString() { + StringBuilder sb = new StringBuilder(); + sb.append(""); + if (startTime != null) { + sb.append("Start time: ").append(dateFormatter.format(startTime)).append("
"); + } + if (endTime != null) { + sb.append("End time: ").append(dateFormatter.format(endTime)).append("
"); + } + sb.append("Total ingest time: ").append(getTotalTimeString()).append("
"); + sb.append("Total errors: ").append(errorsTotal).append("
"); + if (errorsTotal > 0) { + sb.append("Errors per service:"); + for (IngestServiceAbstract service : errors.keySet()) { + final int errorsService = errors.get(service); + sb.append("\t").append(service.getName()).append(": ").append(errorsService).append("
"); + } + } + sb.append(""); + return sb.toString(); + } + void start() { startTime = new Date(); } @@ -491,6 +644,14 @@ public class IngestManager { return endTime.getTime() - startTime.getTime(); } + String getStartTimeString() { + return dateFormatter.format(startTime); + } + + String getEndTimeString() { + return dateFormatter.format(endTime); + } + String getTotalTimeString() { long ms = getTotalTime(); long hours = TimeUnit.MILLISECONDS.toHours(ms); @@ -503,19 +664,19 @@ public class IngestManager { return sb.toString(); } - void addError(IngestServiceAbstract source) { + synchronized void addError(IngestServiceAbstract source) { ++errorsTotal; int curServiceError = errors.get(source); errors.put(source, curServiceError + 1); } } - //ingester worker doing work in background - //in current design, worker runs until queues are consumed - //and if needed, it is restarted when data arrives - private class IngestThread extends SwingWorker { +//ingester worker for fsContent queue +//worker runs until fsContent queue is consumed +//and if needed, new instance is created and started when data arrives + private class IngestFsContentThread extends SwingWorker { - private Logger logger = Logger.getLogger(IngestThread.class.getName()); + private Logger logger = Logger.getLogger(IngestFsContentThread.class.getName()); private ProgressHandle progress; @Override @@ -524,52 +685,21 @@ public class IngestManager { logger.log(Level.INFO, "Starting background processing"); stats.start(); - progress = ProgressHandleFactory.createHandle("Ingesting", new Cancellable() { + progress = ProgressHandleFactory.createHandle("File Ingest", new Cancellable() { @Override public boolean cancel() { - return IngestThread.this.cancel(true); + return IngestFsContentThread.this.cancel(true); } }); progress.start(); - progress.switchToIndeterminate(); - int numImages = getNumImages(); - progress.switchToDeterminate(numImages); - int processedImages = 0; - //process image queue - while (hasNextImage()) { - QueueUnit unit = getNextImage(); - for (IngestServiceImage service : unit.services) { - if (isCancelled()) { - return null; - } - - try { - service.process(unit.content); - } catch (Exception e) { - logger.log(Level.INFO, "Exception from service: " + service.getName(), e); - stats.addError(service); - } - //check if new files enqueued - int newImages = getNumImages(); - if (newImages > numImages) { - numImages = newImages + processedImages + 1; - progress.switchToIndeterminate(); - progress.switchToDeterminate(numImages); - - } - progress.progress("Images (" + service.getName() + ")", ++processedImages); - --numImages; - } - } - progress.switchToIndeterminate(); int numFsContents = getNumFsContents(); progress.switchToDeterminate(numFsContents); int processedFiles = 0; + initMainProgress(numFsContents); //process fscontents queue - progress.progress("Running file ingest services."); while (hasNextFsContent()) { QueueUnit unit = getNextFsContent(); for (IngestServiceFsContent service : unit.services) { @@ -589,9 +719,11 @@ public class IngestManager { numFsContents = newFsContents + processedFiles + 1; progress.switchToIndeterminate(); progress.switchToDeterminate(numFsContents); + initMainProgress(numFsContents); } - progress.progress("Files", ++processedFiles); + progress.progress(unit.content.getName(), ++processedFiles); + updateMainProgress(processedFiles); --numFsContents; } logger.log(Level.INFO, "Done background processing"); @@ -604,10 +736,6 @@ public class IngestManager { super.get(); //block and get all exceptions thrown while doInBackground() //notify services of completion if (!this.isCancelled()) { - for (IngestServiceImage s : imageServices) { - s.complete(); - } - for (IngestServiceFsContent s : fsContentServices) { s.complete(); } @@ -630,28 +758,23 @@ public class IngestManager { stats.end(); progress.finish(); - //TODO display report - logger.log(Level.INFO, "STATS: " + stats.toString()); + if (!this.isCancelled()) { + logger.log(Level.INFO, "Summary Report: " + stats.toString()); + tc.displayReport(stats.toHtmlString()); + } } } - @Override - protected void process(List chunks) { - super.process(chunks); - } - private void handleInterruption() { - for (IngestServiceImage s : imageServices) { - s.stop(); - } - for (IngestServiceFsContent s : fsContentServices) { s.stop(); } //empty queues emptyFsContents(); - emptyImages(); + + //reset main progress bar + initMainProgress(0); } } @@ -691,7 +814,6 @@ public class IngestManager { } catch (CancellationException e) { //task was cancelled handleInterruption(); - } catch (InterruptedException ex) { handleInterruption(); } catch (ExecutionException ex) { @@ -705,13 +827,11 @@ public class IngestManager { //queing end if (this.isCancelled()) { //empty queues - emptyFsContents(); - emptyImages(); + handleInterruption(); } else { //start ingest workers startAll(); } - progress.finish(); tc.enableStartButton(true); } @@ -722,13 +842,29 @@ public class IngestManager { for (Image image : images) { final String imageName = image.getName(); for (IngestServiceAbstract service : services) { + if (isCancelled()) { + return; + } final String serviceName = service.getName(); progress.progress(serviceName + " " + imageName, processed); switch (service.getType()) { case Image: - addImage((IngestServiceImage) service, image); + //enqueue a new instance of image service + //Class serviceClass = service.getClass(); + //serviceClass. + try { + final IngestServiceImage newServiceInstance = (IngestServiceImage) (service.getClass()).newInstance(); + addImage(newServiceInstance, image); + } catch (InstantiationException e) { + logger.log(Level.SEVERE, "Cannot instantiate service: " + service.getName(), e); + } catch (IllegalAccessException e) { + logger.log(Level.SEVERE, "Cannot instantiate service: " + service.getName(), e); + } + + //addImage((IngestServiceImage) service, image); break; case FsContent: + //enqueue the same singleton fscontent service addFsContent((IngestServiceFsContent) service, image); break; default: diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java index 5d9a0daff0..abce9b0fbf 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.ingest; import java.text.SimpleDateFormat; import java.util.Date; -import javax.swing.text.DateFormatter; import org.sleuthkit.autopsy.datamodel.KeyValueThing; /** @@ -82,13 +81,15 @@ public class IngestMessage { StringBuilder sb = new StringBuilder(); sb.append(Long.toString(ID)).append(": "); sb.append("type: ").append(messageType.name()); - sb.append(" source: ").append(source.getName()); + if (source != null) //can be null for manager messages + sb.append(" source: ").append(source.getName()); sb.append(" date: ").append(dateFormat.format(datePosted)); sb.append(" text: ").append(text); if (data != null) sb.append(" data: ").append(data.toString()).append(' '); return sb.toString(); } + @Override public boolean equals(Object obj) { @@ -152,4 +153,9 @@ public class IngestMessage { return im; } + static IngestMessage createManagerMessage(String message) { + IngestMessage im = new IngestMessage(0, MessageType.INFO, null, message); + return im; + } + } diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.form b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.form index 8b378ac374..707f10ce45 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.form +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.form @@ -52,7 +52,6 @@ - diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java index 911c3866dd..2d6e83ca52 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java @@ -135,6 +135,10 @@ public class IngestMessagePanel extends javax.swing.JPanel { //autoscroll messageTable.scrollRectToVisible(messageTable.getCellRect(messageTable.getRowCount() - 1, messageTable.getColumnCount(), true)); } + + public void clearMessages() { + tableModel.clearMessages(); + } private void setVisited(int rowNumber) { tableModel.setVisited(rowNumber); @@ -182,7 +186,13 @@ public class IngestMessagePanel extends javax.swing.JPanel { switch (columnIndex) { case 0: - ret = (Object) entry.message.getSource().getName(); + Object service = entry.message.getSource(); + if (service == null) { + ret = ""; + } + else { + ret = (Object) entry.message.getSource().getName(); + } break; case 1: ret = (Object) entry.message.getText(); @@ -207,7 +217,12 @@ public class IngestMessagePanel extends javax.swing.JPanel { public void addMessage(IngestMessage m) { messageData.add(new TableEntry(m)); int size = messageData.size(); - this.fireTableRowsInserted(size - 1, size); //TODO check + this.fireTableRowsInserted(size - 1, size); + } + + public void clearMessages() { + messageData.clear(); + fireTableDataChanged(); } public void setVisited(int rowNumber) { diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceImage.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceImage.java index 6eb7cfe124..03eb7e6733 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceImage.java +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestServiceImage.java @@ -30,6 +30,9 @@ public interface IngestServiceImage extends IngestServiceAbstract { * notification from manager to process image * The service notifies viewers via IngestManager.postMessage() * and may also write results to the black-board as it is processing. + * + * @param image image to process + * @param controller controller to the worker, to update progress (if determinate) and check if cancelled */ - public void process(Image image); + public void process(Image image, IngestImageWorkerController controller); } \ No newline at end of file diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.form b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.form index b8eeb4467a..4240eb7d0e 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.form +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.form @@ -16,12 +16,12 @@ - + - + @@ -29,7 +29,7 @@ - + @@ -38,7 +38,7 @@ - + @@ -48,13 +48,13 @@ - - - + + + - - + + @@ -64,10 +64,10 @@ - - - - + + + + @@ -76,10 +76,10 @@ - - - - + + + + @@ -93,18 +93,22 @@ + + + + - + - + @@ -115,31 +119,35 @@ - - - - + + + + + + + + + + + - - + + - - - - - - + + + - + @@ -155,7 +163,13 @@ - + + + + + + + @@ -236,6 +250,22 @@ + + + + + + + + + + + + + + + + diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.java index 3eeef7b914..126308054d 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.java +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestTopComponent.java @@ -21,7 +21,10 @@ package org.sleuthkit.autopsy.ingest; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; @@ -32,7 +35,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.BoxLayout; import javax.swing.JCheckBox; -import javax.swing.JFrame; +import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JSlider; import org.openide.util.NbBundle; @@ -47,7 +50,7 @@ import org.sleuthkit.datamodel.TskException; * Top component explorer for the Ingest module. */ public final class IngestTopComponent extends TopComponent implements DataExplorer { - + private static IngestTopComponent instance; private static final Logger logger = Logger.getLogger(IngestTopComponent.class.getName()); private IngestManager manager = null; @@ -55,14 +58,14 @@ public final class IngestTopComponent extends TopComponent implements DataExplor private Map serviceStates; private IngestMessagePanel messagePanel; private ActionListener serviceSelListener = new ActionListener() { - + @Override public void actionPerformed(ActionEvent ev) { JCheckBox box = (JCheckBox) ev.getSource(); serviceStates.put(box.getName(), box.isSelected()); } }; - + private IngestTopComponent() { services = new ArrayList(); serviceStates = new HashMap(); @@ -72,41 +75,41 @@ public final class IngestTopComponent extends TopComponent implements DataExplor setToolTipText(NbBundle.getMessage(IngestTopComponent.class, "HINT_IngestTopComponent")); //putClientProperty(TopComponent.PROP_UNDOCKING_DISABLED, Boolean.TRUE); putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE); - + } - + public static synchronized IngestTopComponent getDefault() { if (instance == null) { instance = new IngestTopComponent(); } return instance; } - + @Override public TopComponent getTopComponent() { return this; } - + @Override public void propertyChange(PropertyChangeEvent evt) { logger.log(Level.INFO, "Unhandled property change: " + evt.getPropertyName()); } - + @Override public int getPersistenceType() { return TopComponent.PERSISTENCE_NEVER; } - + private void customizeComponents() { //custom GUI setup not done by builder freqSlider.setToolTipText("Lower update frequency can optimize performance of certain ingest services, but also reduce real time status feedback"); - + JScrollPane scrollPane = new JScrollPane(servicesPanel); scrollPane.setPreferredSize(this.getSize()); this.add(scrollPane, BorderLayout.CENTER); - + servicesPanel.setLayout(new BoxLayout(servicesPanel, BoxLayout.Y_AXIS)); - + freqSlider.setEnabled(false); @@ -114,13 +117,41 @@ public final class IngestTopComponent extends TopComponent implements DataExplor messagePanel.setOpaque(false); messageFrame.setOpaque(false); - //this.setComponentZOrder(messageFrame, 0); + messageFrame.addComponentListener(new ComponentAdapter() { + + @Override + public void componentResized(ComponentEvent e) { + + super.componentResized(e); + messageFrame.setPreferredSize(messageFrame.getSize()); + } + }); + + //make messageframe on top this.setComponentZOrder(controlPanel, 2); messageFrame.setContentPane(messagePanel); messageFrame.pack(); messageFrame.setVisible(true); - + //handle case change + Case.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(Case.CASE_CURRENT_CASE)) { + Case oldCase = (Case) evt.getOldValue(); + if (oldCase == null) + //nothing to do, not case had been opened + return; + //stop workers if running + if (manager != null) + manager.stopAll(); + //clear inbox + messagePanel.clearMessages(); + } + } + + }); + Collection imageServices = IngestManager.enumerateImageServices(); for (IngestServiceImage service : imageServices) { @@ -132,7 +163,7 @@ public final class IngestTopComponent extends TopComponent implements DataExplor servicesPanel.add(checkbox); serviceStates.put(serviceName, true); } - + Collection fsServices = IngestManager.enumerateFsContentServices(); for (IngestServiceFsContent service : fsServices) { final String serviceName = service.getName(); @@ -163,31 +194,36 @@ public final class IngestTopComponent extends TopComponent implements DataExplor freqSlider = new javax.swing.JSlider(); refreshFreqLabel = new javax.swing.JLabel(); startButton = new javax.swing.JButton(); + refreshFrequencyLabel = new javax.swing.JLabel(); + mainProgressBar = new javax.swing.JProgressBar(); + ingestProgressLabel = new javax.swing.JLabel(); - mainScrollPane.setPreferredSize(new java.awt.Dimension(322, 732)); + mainScrollPane.setPreferredSize(new java.awt.Dimension(322, 749)); - mainPanel.setPreferredSize(new java.awt.Dimension(322, 732)); + mainPanel.setPreferredSize(new java.awt.Dimension(322, 749)); - messageFrame.setBorder(javax.swing.BorderFactory.createMatteBorder(2, 2, 2, 2, new java.awt.Color(204, 204, 255))); + messageFrame.setBorder(new javax.swing.border.LineBorder(javax.swing.UIManager.getDefaults().getColor("InternalFrame.inactiveTitleBackground"), 3, true)); messageFrame.setMaximizable(true); messageFrame.setResizable(true); messageFrame.setTitle(org.openide.util.NbBundle.getMessage(IngestTopComponent.class, "IngestTopComponent.messageFrame.title")); // NOI18N messageFrame.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR)); messageFrame.setFrameIcon(null); + messageFrame.setOpaque(true); + messageFrame.setPreferredSize(new java.awt.Dimension(280, 260)); messageFrame.setVisible(true); javax.swing.GroupLayout messageFrameLayout = new javax.swing.GroupLayout(messageFrame.getContentPane()); messageFrame.getContentPane().setLayout(messageFrameLayout); messageFrameLayout.setHorizontalGroup( messageFrameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 237, Short.MAX_VALUE) + .addGap(0, 274, Short.MAX_VALUE) ); messageFrameLayout.setVerticalGroup( messageFrameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 227, Short.MAX_VALUE) + .addGap(0, 231, Short.MAX_VALUE) ); - topLable.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N + topLable.setFont(new java.awt.Font("Tahoma", 0, 12)); org.openide.awt.Mnemonics.setLocalizedText(topLable, org.openide.util.NbBundle.getMessage(IngestTopComponent.class, "IngestTopComponent.topLable.text")); // NOI18N servicesPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); @@ -226,6 +262,10 @@ public final class IngestTopComponent extends TopComponent implements DataExplor } }); + org.openide.awt.Mnemonics.setLocalizedText(refreshFrequencyLabel, org.openide.util.NbBundle.getMessage(IngestTopComponent.class, "IngestTopComponent.refreshFrequencyLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(ingestProgressLabel, org.openide.util.NbBundle.getMessage(IngestTopComponent.class, "IngestTopComponent.ingestProgressLabel.text")); // NOI18N + javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel); controlPanel.setLayout(controlPanelLayout); controlPanelLayout.setHorizontalGroup( @@ -235,21 +275,31 @@ public final class IngestTopComponent extends TopComponent implements DataExplor .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(controlPanelLayout.createSequentialGroup() .addComponent(topLable) - .addContainerGap(114, Short.MAX_VALUE)) + .addContainerGap(198, Short.MAX_VALUE)) .addGroup(controlPanelLayout.createSequentialGroup() - .addComponent(servicesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGap(31, 31, 31)) + .addComponent(servicesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(115, 115, 115)) .addGroup(controlPanelLayout.createSequentialGroup() .addComponent(startButton) - .addContainerGap(174, Short.MAX_VALUE)) + .addContainerGap(258, Short.MAX_VALUE)) .addGroup(controlPanelLayout.createSequentialGroup() - .addComponent(freqSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(31, Short.MAX_VALUE)))) + .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(mainProgressBar, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(freqSlider, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(115, Short.MAX_VALUE)))) + .addGroup(controlPanelLayout.createSequentialGroup() + .addGap(59, 59, 59) + .addComponent(refreshFrequencyLabel) + .addContainerGap(174, Short.MAX_VALUE)) + .addGroup(controlPanelLayout.createSequentialGroup() + .addGap(59, 59, 59) + .addComponent(ingestProgressLabel) + .addContainerGap(171, Short.MAX_VALUE)) .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(controlPanelLayout.createSequentialGroup() .addGap(74, 74, 74) .addComponent(refreshFreqLabel) - .addContainerGap(77, Short.MAX_VALUE))) + .addContainerGap(161, Short.MAX_VALUE))) ); controlPanelLayout.setVerticalGroup( controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -262,7 +312,13 @@ public final class IngestTopComponent extends TopComponent implements DataExplor .addComponent(startButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(freqSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(288, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(refreshFrequencyLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(mainProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(ingestProgressLabel) + .addContainerGap(228, Short.MAX_VALUE)) .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(controlPanelLayout.createSequentialGroup() .addGap(555, 555, 555) @@ -277,21 +333,21 @@ public final class IngestTopComponent extends TopComponent implements DataExplor .addGroup(mainPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup() + .addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(71, 71, 71)) .addGroup(mainPanelLayout.createSequentialGroup() - .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(71, Short.MAX_VALUE)) - .addGroup(mainPanelLayout.createSequentialGroup() - .addComponent(messageFrame, javax.swing.GroupLayout.DEFAULT_SIZE, 241, Short.MAX_VALUE) - .addGap(71, 71, 71)))) + .addComponent(messageFrame, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(48, 48, 48)))) ); mainPanelLayout.setVerticalGroup( mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(mainPanelLayout.createSequentialGroup() .addContainerGap() - .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 297, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(40, 40, 40) - .addComponent(messageFrame) - .addGap(129, 129, 129)) + .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 357, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(30, 30, 30) + .addComponent(messageFrame, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(127, 127, 127)) ); mainScrollPane.setViewportView(mainPanel); @@ -300,19 +356,20 @@ public final class IngestTopComponent extends TopComponent implements DataExplor this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 322, Short.MAX_VALUE) + .addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 340, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 732, Short.MAX_VALUE) + .addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 771, Short.MAX_VALUE) ); }// //GEN-END:initComponents private void startButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startButtonActionPerformed - if (manager == null) + if (manager == null) { return; - + } + //pick the services List servicesToStart = new ArrayList(); for (IngestServiceAbstract service : services) { @@ -335,30 +392,34 @@ public final class IngestTopComponent extends TopComponent implements DataExplor images.add(image); } catch (TskException e) { logger.log(Level.SEVERE, "Error ingesting image, can't retrieve image id: " + Integer.toString(imageId), e); - + } catch (SQLException e) { logger.log(Level.SEVERE, "Error ingesting image, can't retrieve image id: " + Integer.toString(imageId), e); } } - manager.execute(servicesToStart, images); + if (! services.isEmpty() && ! images.isEmpty()) + manager.execute(servicesToStart, images); }//GEN-LAST:event_startButtonActionPerformed - + private void freqSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_freqSliderStateChanged JSlider source = (JSlider) evt.getSource(); if (!source.getValueIsAdjusting()) { final int refresh = (int) source.getValue(); manager.setUpdateFrequency(refresh); - + } }//GEN-LAST:event_freqSliderStateChanged // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel controlPanel; private javax.swing.JSlider freqSlider; + private javax.swing.JLabel ingestProgressLabel; private javax.swing.JPanel mainPanel; + private javax.swing.JProgressBar mainProgressBar; private javax.swing.JScrollPane mainScrollPane; private javax.swing.JInternalFrame messageFrame; private javax.swing.JLabel refreshFreqLabel; + private javax.swing.JLabel refreshFrequencyLabel; private javax.swing.JPanel servicesPanel; private javax.swing.JButton startButton; private javax.swing.JLabel topLable; @@ -369,7 +430,7 @@ public final class IngestTopComponent extends TopComponent implements DataExplor logger.log(Level.INFO, "IngestTopComponent opened()"); manager = new IngestManager(this); } - + @Override public void componentClosed() { logger.log(Level.INFO, "IngestTopComponent closed()"); @@ -378,33 +439,42 @@ public final class IngestTopComponent extends TopComponent implements DataExplor void enableStartButton(boolean enable) { startButton.setEnabled(enable); } - + void writeProperties(java.util.Properties p) { // better to version settings since initial version as advocated at // http://wiki.apidesign.org/wiki/PropertyFiles p.setProperty("version", "1.0"); - + } - + void readProperties(java.util.Properties p) { String version = p.getProperty("version"); - + } /** * Display ingest summary report in some dialog */ void displayReport(String ingestReport) { - //TODO widget - logger.log(Level.INFO, "INGEST REPORT: " + ingestReport); + JOptionPane.showMessageDialog( + null, + ingestReport, + "File Ingest Summary", + JOptionPane.INFORMATION_MESSAGE); } /** * Display IngestMessage from service (forwarded by IngestManager) */ void displayMessage(IngestMessage ingestMessage) { - //TODO widget - logger.log(Level.INFO, "INGEST MESSAGE: " + ingestMessage.toString()); messagePanel.addMessage(ingestMessage); } + + void initProgress(int maximum) { + this.mainProgressBar.setMaximum(maximum); + } + + void updateProgress(int progress) { + this.mainProgressBar.setValue(progress); + } } diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/example/ExampleImageIngestService.java b/Ingest/src/org/sleuthkit/autopsy/ingest/example/ExampleImageIngestService.java index f8fdd5086a..8865d82d9a 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/example/ExampleImageIngestService.java +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/example/ExampleImageIngestService.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.ingest.example; import java.util.logging.Level; import java.util.logging.Logger; +import org.sleuthkit.autopsy.ingest.IngestImageWorkerController; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestMessage; import org.sleuthkit.autopsy.ingest.IngestMessage.MessageType; @@ -33,26 +34,53 @@ import org.sleuthkit.datamodel.Image; public final class ExampleImageIngestService implements IngestServiceImage { private static final Logger logger = Logger.getLogger(ExampleImageIngestService.class.getName()); - private static ExampleImageIngestService instance = null; + private static ExampleImageIngestService defaultInstance = null; private IngestManager manager; - private static int messageId = 0; + //public constructor is required + //as multiple instances are created for processing multiple images simultenously + public ExampleImageIngestService() { + } + + //default instance used for service registration public static synchronized ExampleImageIngestService getDefault() { - if (instance == null) { - instance = new ExampleImageIngestService(); + if (defaultInstance == null) { + defaultInstance = new ExampleImageIngestService(); } - return instance; + return defaultInstance; } @Override - public void process(Image image) { + public void process(Image image, IngestImageWorkerController controller) { + logger.log(Level.INFO, "process() " + this.toString()); + manager.postMessage(IngestMessage.createMessage(++messageId, MessageType.INFO, this, "Processing " + image.getName())); //service specific Image processing code here - try { - Thread.sleep(5000); - } catch (InterruptedException e) { + //example: + + //if we know amount of work units, we can switch to determinate and update progress bar + int filesToProcess = 100; + controller.switchToDeterminate(filesToProcess); + int processedFiles = 0; + + while (filesToProcess-- > 0) { + + //check if should terminate on every loop iteration + if (controller.isCancelled()) { + return; + } + try { + //do the work + Thread.sleep(500); + //post message to user if found something interesting + manager.postMessage(IngestMessage.createMessage(processedFiles, MessageType.INFO, this, "Processed " + image.getName() + ": " + Integer.toString(processedFiles))); + + //update progress + controller.progress(++processedFiles); + } catch (InterruptedException e) { + } } @@ -60,10 +88,12 @@ public final class ExampleImageIngestService implements IngestServiceImage { @Override public void complete() { - logger.log(Level.INFO, "complete()"); - manager.postMessage(IngestMessage.createMessage(++messageId, MessageType.INFO, this, "COMPLETE")); - - //service specific cleanup due completion here + logger.log(Level.INFO, "complete() " + this.toString()); + + final IngestMessage msg = IngestMessage.createMessage(++messageId, MessageType.INFO, this, "completed image processing"); + manager.postMessage(msg); + + //service specific cleanup due to completion here } @Override @@ -73,18 +103,18 @@ public final class ExampleImageIngestService implements IngestServiceImage { @Override public void init(IngestManager manager) { - logger.log(Level.INFO, "init()"); + logger.log(Level.INFO, "init() " + this.toString()); this.manager = manager; //service specific initialization here - + } @Override public void stop() { logger.log(Level.INFO, "stop()"); - - //service specific cleanup due interruption here + + //service specific cleanup due to interruption here } @Override diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/layer.xml b/Ingest/src/org/sleuthkit/autopsy/ingest/layer.xml index b324999edb..d507c6978d 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/layer.xml +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/layer.xml @@ -8,17 +8,18 @@ - + diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestService.java index a152a75f06..35760639da 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestService.java @@ -49,7 +49,7 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent Thread.sleep(100); } catch (InterruptedException e) {} - manager.postMessage(IngestMessage.createMessage(1, MessageType.INFO, this, "Processing " + fsContent.getName())); + //manager.postMessage(IngestMessage.createMessage(1, MessageType.INFO, this, "Processing " + fsContent.getName())); }