mirror of
https://github.com/elisspace/autopsy.git
synced 2026-09-24 19:46:51 +00:00
Merge branch 'collaborative' of https://github.com/sleuthkit/autopsy into add_data_source
This commit is contained in:
@@ -21,69 +21,70 @@ package org.sleuthkit.autopsy.corecomponentinterfaces;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
/*
|
||||
* Defines an interface used by the Add DataSource wizard to discover different
|
||||
* Data SourceProcessors.
|
||||
/**
|
||||
* Interface used by the Add DataSource wizard to allow different
|
||||
* types of data sources to be added to a case. Examples of data
|
||||
* sources include disk images, local files, etc.
|
||||
*
|
||||
* Each data source may have its unique attributes and may need to be processed
|
||||
* differently.
|
||||
*
|
||||
* The DataSourceProcessor interface defines a uniform mechanism for the Autopsy UI
|
||||
* The interface provides a uniform mechanism for the Autopsy UI
|
||||
* to:
|
||||
* - collect details for the data source to be processed.
|
||||
* - Process the data source in the background
|
||||
* - Be notified when the processing is complete
|
||||
* - Collect details from the user about the data source to be processed.
|
||||
* - Process the data source in the background and add data to the database
|
||||
* - Provides progress feedback to the user / UI.
|
||||
*/
|
||||
public interface DataSourceProcessor {
|
||||
|
||||
/*
|
||||
/**
|
||||
* The DSP Panel may fire Property change events
|
||||
* The caller must enure to add itself as a listener and
|
||||
* then react appropriately to the events
|
||||
*/
|
||||
enum DSP_PANEL_EVENT {
|
||||
|
||||
UPDATE_UI, // the content of JPanel has changed that MAY warrant updates to the caller UI
|
||||
FOCUS_NEXT // the caller UI may move focus the the next UI element, following the panel.
|
||||
UPDATE_UI, ///< the content of JPanel has changed that MAY warrant updates to the caller UI
|
||||
FOCUS_NEXT ///< the caller UI may move focus the the next UI element, following the panel.
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of Data Source it handles.
|
||||
* This name gets displayed in the drop-down listbox
|
||||
**/
|
||||
*/
|
||||
String getDataSourceType();
|
||||
|
||||
/**
|
||||
* Returns the picker panel to be displayed along with any other
|
||||
* runtime options supported by the data source handler.
|
||||
**/
|
||||
* runtime options supported by the data source handler. The
|
||||
* DSP is responsible for storing the settings so that a later
|
||||
* call to run() will have the user-specified settings.
|
||||
*
|
||||
* Should be less than 544 pixels wide and 173 pixels high.
|
||||
*/
|
||||
JPanel getPanel();
|
||||
|
||||
/**
|
||||
* Called to validate the input data in the panel.
|
||||
* Returns true if no errors, or
|
||||
* Returns false if there is an error.
|
||||
**/
|
||||
*/
|
||||
boolean isPanelValid();
|
||||
|
||||
/**
|
||||
* Called to invoke the handling of Data source in the background.
|
||||
* Returns after starting the background thread
|
||||
* @param settings wizard settings to read/store properties
|
||||
* @param progressPanel progress panel to be updated while processing
|
||||
* Called to invoke the handling of data source in the background.
|
||||
* Returns after starting the background thread.
|
||||
*
|
||||
**/
|
||||
* @param progressPanel progress panel to be updated while processing
|
||||
* @param dspCallback Contains the callback method DataSourceProcessorCallback.done() that the DSP must call when the background thread finishes with errors and status.
|
||||
*/
|
||||
void run(DataSourceProcessorProgressMonitor progressPanel, DataSourceProcessorCallback dspCallback);
|
||||
|
||||
|
||||
/**
|
||||
* Called to cancel the background processing.
|
||||
**/
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/**
|
||||
* Called to reset/reinitialize the DSP.
|
||||
**/
|
||||
*/
|
||||
void reset();
|
||||
}
|
||||
|
||||
+39
-30
@@ -16,7 +16,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.sleuthkit.autopsy.corecomponentinterfaces;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
@@ -25,42 +24,52 @@ import org.sleuthkit.datamodel.Content;
|
||||
|
||||
/**
|
||||
* Abstract class for a callback for a DataSourceProcessor.
|
||||
*
|
||||
* Ensures that DSP invokes the caller overridden method, doneEDT(),
|
||||
* in the EDT thread.
|
||||
*
|
||||
*
|
||||
* Ensures that DSP invokes the caller overridden method, doneEDT(), in the EDT
|
||||
* thread.
|
||||
*
|
||||
*/
|
||||
public abstract class DataSourceProcessorCallback {
|
||||
|
||||
public enum DataSourceProcessorResult
|
||||
{
|
||||
NO_ERRORS,
|
||||
CRITICAL_ERRORS,
|
||||
NONCRITICAL_ERRORS,
|
||||
|
||||
public enum DataSourceProcessorResult {
|
||||
NO_ERRORS, ///< No errors were encountered while ading the data source
|
||||
CRITICAL_ERRORS, ///< No data was added to the database. There were fundamental errors processing the data (such as no data or system failure).
|
||||
NONCRITICAL_ERRORS, ///< There was data added to the database, but there were errors from data corruption or a small number of minor issues.
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Invoke the caller supplied callback function on the EDT thread
|
||||
/**
|
||||
* Called by a DSP implementation when it is done adding a data source
|
||||
* to the database. Users of the DSP can override this method if they do
|
||||
* not want to be notified on the EDT. Otherwise, this method will call
|
||||
* doneEDT() with the same arguments.
|
||||
* @param result Code for status
|
||||
* @param errList List of error strings
|
||||
* @param newContents List of root Content objects that were added to database. Typically only one is given.
|
||||
*/
|
||||
public void done(DataSourceProcessorResult result, List<String> errList, List<Content> newContents)
|
||||
{
|
||||
|
||||
public void done(DataSourceProcessorResult result, List<String> errList, List<Content> newContents) {
|
||||
|
||||
final DataSourceProcessorResult resultf = result;
|
||||
final List<String> errListf = errList;
|
||||
final List<Content> newContentsf = newContents;
|
||||
|
||||
// Invoke doneEDT() that runs on the EDT .
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doneEDT(resultf, errListf, newContentsf );
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Invoke doneEDT() that runs on the EDT .
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doneEDT(resultf, errListf, newContentsf);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* calling code overrides to provide its own calllback
|
||||
*/
|
||||
public abstract void doneEDT(DataSourceProcessorResult result, List<String> errList, List<Content> newContents);
|
||||
|
||||
/**
|
||||
* Called by done() if the default implementation is used. Users of DSPs
|
||||
* that have UI updates to do after the DSP is finished adding the DS can
|
||||
* implement this method to receive the updates on the EDT.
|
||||
*
|
||||
* @param result Code for status
|
||||
* @param errList List of error strings
|
||||
* @param newContents List of root Content objects that were added to database. Typically only one is given.
|
||||
*/
|
||||
public abstract void doneEDT(DataSourceProcessorResult result, List<String> errList, List<Content> newContents);
|
||||
};
|
||||
|
||||
+2
-2
@@ -18,10 +18,10 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.corecomponentinterfaces;
|
||||
|
||||
/*
|
||||
/**
|
||||
* An GUI agnostic DataSourceProcessorProgressMonitor interface for DataSourceProcesssors to
|
||||
* indicate progress.
|
||||
* It models after a JProgressbar though it could use any underlying implementation
|
||||
* It models after a JProgressbar though it could use any underlying implementation (or NoOps)
|
||||
*/
|
||||
public interface DataSourceProcessorProgressMonitor {
|
||||
|
||||
|
||||
@@ -78,8 +78,10 @@ import org.sleuthkit.datamodel.TskData;
|
||||
@ServiceProvider(service = FrameCapture.class)
|
||||
})
|
||||
public class FXVideoPanel extends MediaViewVideoPanel {
|
||||
|
||||
private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".mpg", ".mpeg"}; //NON-NLS
|
||||
|
||||
// Refer to https://docs.oracle.com/javafx/2/api/javafx/scene/media/package-summary.html
|
||||
// for Javafx supported formats
|
||||
private static final String[] EXTENSIONS = new String[]{".m4v", ".fxm", ".flv", ".m3u8", ".mp4", ".aif", ".aiff", ".mp3", "m4a", ".wav"}; //NON-NLS
|
||||
private static final List<String> MIMETYPES = Arrays.asList("audio/x-aiff", "video/x-javafx", "video/x-flv", "application/vnd.apple.mpegurl", " audio/mpegurl", "audio/mpeg", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
|
||||
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
|
||||
|
||||
@@ -478,6 +480,10 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
||||
pauseButton.setOnAction(new EventHandler<ActionEvent>() {
|
||||
@Override
|
||||
public void handle(ActionEvent e) {
|
||||
if (mediaPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Status status = mediaPlayer.getStatus();
|
||||
|
||||
switch (status) {
|
||||
@@ -496,7 +502,7 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
||||
// If the MediaPlayer is in an unexpected state, stop playback.
|
||||
mediaPlayer.stop();
|
||||
setInfoLabelText(NbBundle.getMessage(this.getClass(),
|
||||
"FXVideoPanel.pauseButton.infoLabel.playbackErr"));
|
||||
"FXVideoPanel.pauseButton.infoLabel.playbackErr"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -505,6 +511,10 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
||||
stopButton.setOnAction(new EventHandler<ActionEvent>() {
|
||||
@Override
|
||||
public void handle(ActionEvent e) {
|
||||
if (mediaPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mediaPlayer.stop();
|
||||
}
|
||||
});
|
||||
@@ -512,6 +522,10 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
||||
progressSlider.valueProperty().addListener(new InvalidationListener() {
|
||||
@Override
|
||||
public void invalidated(Observable o) {
|
||||
if (mediaPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (progressSlider.isValueChanging()) {
|
||||
mediaPlayer.seek(duration.multiply(progressSlider.getValue() / 100.0));
|
||||
}
|
||||
@@ -559,6 +573,9 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
||||
* media.
|
||||
*/
|
||||
private void updateProgress() {
|
||||
if (mediaPlayer == null) {
|
||||
return;
|
||||
}
|
||||
Duration currentTime = mediaPlayer.getCurrentTime();
|
||||
updateSlider(currentTime);
|
||||
updateTime(currentTime);
|
||||
@@ -634,6 +651,10 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mediaPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
duration = mediaPlayer.getMedia().getDuration();
|
||||
long durationInMillis = (long) mediaPlayer.getMedia().getDuration().toMillis();
|
||||
|
||||
@@ -657,6 +678,10 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mediaPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Duration beginning = mediaPlayer.getStartTime();
|
||||
mediaPlayer.stop();
|
||||
mediaPlayer.pause();
|
||||
|
||||
@@ -33,11 +33,9 @@ import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.ImageIcon;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
@@ -55,7 +53,11 @@ public class ImageUtils {
|
||||
private static final Logger logger = Logger.getLogger(ImageUtils.class.getName());
|
||||
private static final Image DEFAULT_ICON = new ImageIcon("/org/sleuthkit/autopsy/images/file-icon.png").getImage(); //NON-NLS
|
||||
private static final List<String> SUPP_EXTENSIONS = Arrays.asList(ImageIO.getReaderFileSuffixes());
|
||||
private static final List<String> SUPP_MIME_TYPES = Arrays.asList(ImageIO.getReaderMIMETypes());
|
||||
private static final List<String> SUPP_MIME_TYPES = new ArrayList(Arrays.asList(ImageIO.getReaderMIMETypes()));
|
||||
static {
|
||||
SUPP_MIME_TYPES.add("image/x-ms-bmp");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default Icon, which is the icon for a file.
|
||||
* @return
|
||||
@@ -88,14 +90,17 @@ public class ImageUtils {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the file type is known and we don't support it, bail
|
||||
if (attributes.size() > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error while getting file signature from blackboard.", ex); //NON-NLS
|
||||
}
|
||||
|
||||
final String extension = f.getNameExtension();
|
||||
|
||||
// if we have an extension, check it
|
||||
final String extension = f.getNameExtension();
|
||||
if (extension.equals("") == false) {
|
||||
// Note: thumbnail generator only supports JPG, GIF, and PNG for now
|
||||
if (SUPP_EXTENSIONS.contains(extension)) {
|
||||
@@ -109,7 +114,8 @@ public class ImageUtils {
|
||||
|
||||
|
||||
/**
|
||||
* Get an icon of a specified size.
|
||||
* Get a thumbnail of a specified size. Generates the image if it is
|
||||
* not already cached.
|
||||
*
|
||||
* @param content
|
||||
* @param iconSize
|
||||
@@ -118,6 +124,7 @@ public class ImageUtils {
|
||||
public static Image getIcon(Content content, int iconSize) {
|
||||
Image icon;
|
||||
// If a thumbnail file is already saved locally
|
||||
// @@@ Bug here in that we do not refer to size in the cache.
|
||||
File file = getFile(content.getId());
|
||||
if (file.exists()) {
|
||||
try {
|
||||
@@ -125,7 +132,7 @@ public class ImageUtils {
|
||||
if (bicon == null) {
|
||||
icon = DEFAULT_ICON;
|
||||
} else if (bicon.getWidth() != iconSize) {
|
||||
icon = generateAndSaveIcon(content, iconSize);
|
||||
icon = generateAndSaveIcon(content, iconSize, file);
|
||||
} else {
|
||||
icon = bicon;
|
||||
}
|
||||
@@ -134,18 +141,17 @@ public class ImageUtils {
|
||||
icon = DEFAULT_ICON;
|
||||
}
|
||||
} else { // Make a new icon
|
||||
icon = generateAndSaveIcon(content, iconSize);
|
||||
icon = generateAndSaveIcon(content, iconSize, file);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached file of the icon. Generates the icon and its file if it
|
||||
* doesn't already exist, so this method guarantees to return a file that
|
||||
* exists.
|
||||
* Get a thumbnail of a specified size. Generates the image if it is
|
||||
* not already cached.
|
||||
* @param content
|
||||
* @param iconSize
|
||||
* @return
|
||||
* @return File object for cached image. Is guaranteed to exist.
|
||||
*/
|
||||
public static File getIconFile(Content content, int iconSize) {
|
||||
if (getIcon(content, iconSize) != null) {
|
||||
@@ -155,13 +161,12 @@ public class ImageUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached file of the content object with the given id.
|
||||
*
|
||||
* The returned file may not exist.
|
||||
* Get a file object for where the cached icon should exist. The returned file may not exist.
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
// TODO: This should be private and be renamed to something like getCachedThumbnailLocation().
|
||||
public static File getFile(long id) {
|
||||
return new File(Case.getCurrentCase().getCacheDirectory() + File.separator + id + ".png");
|
||||
}
|
||||
@@ -223,18 +228,24 @@ public class ImageUtils {
|
||||
}
|
||||
|
||||
|
||||
private static Image generateAndSaveIcon(Content content, int iconSize) {
|
||||
/**
|
||||
* Generate an icon and save it to specified location.
|
||||
* @param content File to generate icon for
|
||||
* @param iconSize
|
||||
* @param saveFile Location to save thumbnail to
|
||||
* @return Generated icon or null on error
|
||||
*/
|
||||
private static Image generateAndSaveIcon(Content content, int iconSize, File saveFile) {
|
||||
Image icon = null;
|
||||
try {
|
||||
icon = generateIcon(content, iconSize);
|
||||
if (icon == null) {
|
||||
return DEFAULT_ICON;
|
||||
} else {
|
||||
File f = getFile(content.getId());
|
||||
if (f.exists()) {
|
||||
f.delete();
|
||||
if (saveFile.exists()) {
|
||||
saveFile.delete();
|
||||
}
|
||||
ImageIO.write((BufferedImage) icon, "png", getFile(content.getId())); //NON-NLS
|
||||
ImageIO.write((BufferedImage) icon, "png", saveFile); //NON-NLS
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Could not write cache thumbnail: " + content, ex); //NON-NLS
|
||||
@@ -243,7 +254,7 @@ public class ImageUtils {
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a scaled image
|
||||
* Generate and return a scaled image
|
||||
*/
|
||||
private static BufferedImage generateIcon(Content content, int iconSize) {
|
||||
|
||||
|
||||
@@ -44,7 +44,14 @@ class CollapseAction extends AbstractAction {
|
||||
// Collapse all
|
||||
|
||||
BeanTreeView tree = DirectoryTreeTopComponent.findInstance().getTree();
|
||||
collapseAll(tree, selectedNode[0]);
|
||||
if(selectedNode.length != 0) {
|
||||
collapseSelectedNode(tree, selectedNode[0]);
|
||||
} else {
|
||||
// If no node is selected, all the level-2 nodes (children of the
|
||||
// root node) are collapsed.
|
||||
for(Node childOfRoot: em.getRootContext().getChildren().getNodes())
|
||||
collapseSelectedNode(tree, childOfRoot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,13 +60,13 @@ class CollapseAction extends AbstractAction {
|
||||
* @param tree the given tree
|
||||
* @param currentNode the current selectedNode
|
||||
*/
|
||||
private void collapseAll(BeanTreeView tree, Node currentNode) {
|
||||
private void collapseSelectedNode(BeanTreeView tree, Node currentNode) {
|
||||
|
||||
Children c = currentNode.getChildren();
|
||||
|
||||
for (Node next : c.getNodes()) {
|
||||
if (tree.isExpanded(next)) {
|
||||
this.collapseAll(tree, next);
|
||||
this.collapseSelectedNode(tree, next);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Directory;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
|
||||
/**
|
||||
* This class sets the actions for the nodes in the directory tree and creates
|
||||
@@ -106,22 +107,35 @@ class DirectoryTreeFilterNode extends FilterNode {
|
||||
actions.add(ExtractAction.getInstance());
|
||||
}
|
||||
|
||||
// file search action
|
||||
final Image img = this.getLookup().lookup(Image.class);
|
||||
if (img != null) {
|
||||
actions.add(new FileSearchAction(
|
||||
NbBundle.getMessage(this.getClass(), "DirectoryTreeFilterNode.action.openFileSrcByAttr.text")));
|
||||
|
||||
VirtualDirectory virtualDirectory = this.getLookup().lookup(VirtualDirectory.class);
|
||||
// determine if the virtualDireory is at root-level (Logical File Set).
|
||||
boolean isRootVD = false;
|
||||
if (virtualDirectory != null) {
|
||||
try {
|
||||
if (virtualDirectory.getParent() == null) {
|
||||
isRootVD = true;
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error determining the parent of the virtual directory", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
//ingest action
|
||||
actions.add(new AbstractAction(
|
||||
NbBundle.getMessage(this.getClass(), "DirectoryTreeFilterNode.action.runIngestMods.text")) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final RunIngestModulesDialog ingestDialog = new RunIngestModulesDialog(Collections.<Content>singletonList(content));
|
||||
ingestDialog.display();
|
||||
}
|
||||
});
|
||||
// 'run ingest' action and 'file search' action are added only if the
|
||||
// selected node is img node or a root level virtual directory.
|
||||
if (img != null || isRootVD) {
|
||||
actions.add(new FileSearchAction(
|
||||
NbBundle.getMessage(this.getClass(), "DirectoryTreeFilterNode.action.openFileSrcByAttr.text")));
|
||||
actions.add(new AbstractAction(
|
||||
NbBundle.getMessage(this.getClass(), "DirectoryTreeFilterNode.action.runIngestMods.text")) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final RunIngestModulesDialog ingestDialog = new RunIngestModulesDialog(Collections.<Content>singletonList(content));
|
||||
ingestDialog.display();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//check if delete actions should be added
|
||||
|
||||
@@ -98,7 +98,7 @@ class SampleFileIngestModule implements FileIngestModule {
|
||||
|
||||
@Override
|
||||
public IngestModule.ProcessResult process(AbstractFile file) {
|
||||
if (attrId != -1) {
|
||||
if (attrId == -1) {
|
||||
return IngestModule.ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -248,7 +248,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
||||
*/
|
||||
@Override
|
||||
public void shutDown() {
|
||||
if (refCounter.decrementAndGet(this.context.getJobId()) == 0) {
|
||||
if (this.context != null && refCounter.decrementAndGet(this.context.getJobId()) == 0) {
|
||||
try {
|
||||
// The last instance of this module for an ingest job cleans out
|
||||
// the working paths map entry for the job and deletes the temp dir.
|
||||
|
||||
Reference in New Issue
Block a user