improve disposal of resources

This commit is contained in:
jmillman 2015-12-30 15:45:51 -05:00
parent 8bb612ab2c
commit 8f32064c2d
5 changed files with 95 additions and 129 deletions

View File

@ -623,10 +623,13 @@ public class ImageUtils {
ImageReader reader = readers.next();
reader.setInput(input);
try {
return propertyExtractor.extract(reader);
} catch (IOException ex) {
ImageUtils.logContentError(LOGGER, Level.WARNING, errorTemplate + ex.toString(), file);
throw ex;
} finally {
reader.dispose();
}
} else {
IIOException iioException = newImageReaderException(file);
@ -672,7 +675,7 @@ public class ImageUtils {
if (nonNull(cachedThumbnail) && cachedThumbnail.getWidth() == iconSize) {
return SwingFXUtils.toFXImage(cachedThumbnail, null);
}
} catch (Exception ex) {
} catch (IOException ex) {
logContentError(logger, Level.WARNING, "ImageIO had a problem reading thumbnail for image {0}: " + ex.toString(), file);
}
}
@ -747,6 +750,7 @@ public class ImageUtils {
ReadImageTask(AbstractFile file) {
super(file);
updateMessage(Bundle.LoadImageTask_mesageText(file.getName()));
}
@Override
@ -754,7 +758,7 @@ public class ImageUtils {
"# {0} - file name",
"LoadImageTask.mesageText=Reading image: {0}"})
protected javafx.scene.image.Image call() throws Exception {
updateMessage(Bundle.LoadImageTask_mesageText(file.getName()));
return readImage();
}
}
@ -762,18 +766,12 @@ public class ImageUtils {
static private abstract class ReadImageTaskBase extends Task<javafx.scene.image.Image> implements IIOReadProgressListener {
final AbstractFile file;
private volatile BufferedImage bufferedImage = null;
private ImageReader reader;
ReadImageTaskBase(AbstractFile file) {
this.file = file;
}
public BufferedImage getBufferedImage() throws InterruptedException, ExecutionException {
get();
return bufferedImage;
}
protected javafx.scene.image.Image readImage() throws IOException {
try (InputStream inputStream = new BufferedInputStream(new ReadContentInputStream(file));) {
if (ImageUtils.isGIF(file)) {
@ -803,23 +801,23 @@ public class ImageUtils {
*/
ImageReadParam param = reader.getDefaultReadParam();
bufferedImage = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0));
BufferedImage bufferedImage = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0));
param.setDestination(bufferedImage);
try {
reader.read(0, param);
bufferedImage = reader.read(0, param);
if (isCancelled()) {
return null;
}
} catch (IOException iOException) {
// Ignore this exception or display a warning or similar, for exceptions happening during decoding
logContentError(logger, Level.WARNING, "ImageIO could not read {0}. It may be unsupported or corrupt: " + iOException.toString(), file);
} finally {
reader.removeIIOReadProgressListener(this);
reader.dispose();
}
reader.removeIIOReadProgressListener(this);
reader.dispose();
return SwingFXUtils.toFXImage(bufferedImage, null);
} else {
throw newImageReaderException(file);
}
}
}

View File

@ -66,7 +66,7 @@ public enum ThumbnailCache {
* in memory cache. keeps at most 1000 items each for up to 10 minutes.
* items may be garbage collected if there are no strong references to them.
*/
private final Cache<Long, Optional<Image>> cache = CacheBuilder.newBuilder()
private final Cache<Long, Image> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.softValues()
.expireAfterAccess(10, TimeUnit.MINUTES).build();
@ -99,7 +99,7 @@ public enum ThumbnailCache {
@Nullable
public Image get(DrawableFile<?> file) {
try {
return cache.get(file.getId(), () -> load(file)).orElse(null);
return cache.get(file.getId(), () -> load(file));
} catch (UncheckedExecutionException | CacheLoader.InvalidCacheLoadException | ExecutionException ex) {
LOGGER.log(Level.WARNING, "Failed to load thumbnail for file: " + file.getName(), ex.getCause());
return null;
@ -124,12 +124,12 @@ public enum ThumbnailCache {
*
* @return an (possibly empty) optional containing a thumbnail
*/
private Optional<Image> load(DrawableFile<?> file) {
private Image load(DrawableFile<?> file) {
if (FileTypeUtils.isGIF(file)) {
//directly read gif to preserve potential animation,
//NOTE: not saved to disk!
return Optional.of(new Image(new BufferedInputStream(new ReadContentInputStream(file.getAbstractFile())), MAX_THUMBNAIL_SIZE, MAX_THUMBNAIL_SIZE, true, true));
return new Image(new BufferedInputStream(new ReadContentInputStream(file.getAbstractFile())), MAX_THUMBNAIL_SIZE, MAX_THUMBNAIL_SIZE, true, true);
}
BufferedImage thumbnail = getCacheFile(file).map(cachFile -> {
@ -160,7 +160,7 @@ public enum ThumbnailCache {
jfxthumbnail = SwingFXUtils.toFXImage(thumbnail, null);
}
return Optional.ofNullable(jfxthumbnail); //return icon, or null if generation failed
return jfxthumbnail; //return icon, or null if generation failed
}
/**
@ -182,12 +182,12 @@ public enum ThumbnailCache {
}
public Task<Image> getThumbnailTask(DrawableFile<?> file) {
final Optional<Image> option = cache.getIfPresent(file.getId());
if (option != null && option.isPresent()) {
final Image thumbnail = cache.getIfPresent(file.getId());
if (thumbnail != null) {
return new Task<Image>() {
@Override
protected Image call() throws Exception {
return option.get();
return thumbnail;
}
};
}
@ -196,7 +196,7 @@ public enum ThumbnailCache {
switch (newGetThumbnailTask.getState()) {
case SUCCEEDED:
try {
cache.put(Long.MIN_VALUE, Optional.of(newGetThumbnailTask.get()));
cache.put(Long.MIN_VALUE, newGetThumbnailTask.get());
} catch (InterruptedException | ExecutionException ex) {
Exceptions.printStackTrace(ex);
}

View File

@ -60,9 +60,9 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
private static final Logger LOGGER = Logger.getLogger(DrawableUIBase.class.getName());
@FXML
BorderPane imageBorder;
BorderPane imageBorder;
@FXML
ImageView imageView;
ImageView imageView;
private final ImageGalleryController controller;
@ -118,33 +118,41 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
synchronized public void setFile(Long newFileID) {
if (getFileID().isPresent()) {
if (Objects.equals(newFileID, getFileID().get()) == false) {
// if (Objects.nonNull(newFileID)) {
setFileHelper(newFileID);
// }
}
} else {//if (Objects.nonNull(newFileID)) {
} else {
setFileHelper(newFileID);
}
}
synchronized protected void updateContent() {
if (getFile().isPresent() == false) {
Platform.runLater(() -> imageBorder.setCenter(null));
} else {
if (getFile().isPresent()) {
doReadImageTask(getFile().get());
}
}
synchronized Node doReadImageTask(DrawableFile<?> file) {
disposeContent();
Task<Image> myTask = newReadImageTask(file);
imageTask = myTask;
Node progressNode = newProgressIndicator(myTask);
Platform.runLater(() -> imageBorder.setCenter(progressNode));
//called on fx thread
imageTask.setOnSucceeded(succeeded -> showImage(file, myTask));
imageTask.setOnFailed(failed -> showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file));
myTask.setOnSucceeded(succeeded -> {
showImage(file, myTask);
synchronized (DrawableUIBase.this) {
imageTask = null;
}
});
myTask.setOnFailed(failed -> {
showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
synchronized (DrawableUIBase.this) {
imageTask = null;
}
});
myTask.setOnCancelled(cancelled -> {
disposeContent();
});
exec.execute(myTask);
return progressNode;
@ -155,8 +163,10 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
imageTask.cancel();
}
imageTask = null;
Platform.runLater(() -> imageView.setImage(null));
Platform.runLater(() -> {
imageView.setImage(null);
imageBorder.setCenter(null);
});
}
/**
@ -171,7 +181,7 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
void showImage(DrawableFile<?> file, Task<Image> imageTask) {
private void showImage(DrawableFile<?> file, Task<Image> imageTask) {
//Note that all error conditions are allready logged in readImageTask.succeeded()
try {
Image fxImage = imageTask.get();
@ -201,57 +211,4 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
}
abstract Task<Image> newReadImageTask(DrawableFile<?> file);
abstract class CachedLoaderTask<X, Y extends DrawableFile<?>> extends Task<X> {
protected final Y file;
public CachedLoaderTask(Y file) {
this.file = file;
}
@Override
protected X call() throws Exception {
return (isCancelled() == false) ? load() : null;
}
abstract X load() throws Exception;
@Override
protected void succeeded() {
super.succeeded();
if (isCancelled() == false) {
try {
saveToCache(get());
} catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.WARNING, "Failed to cache content for" + file.getName(), ex);
}
}
}
@Override
protected void failed() {
super.failed();
LOGGER.log(Level.SEVERE, "Failed to cache content for" + file.getName(), getException());
}
abstract void saveToCache(X result);
}
// class ThumbnailLoaderTask extends CachedLoaderTask<Image, DrawableFile<?>> {
//
// ThumbnailLoaderTask(DrawableFile<?> file) {
// super(file);
// }
//
// @Override
// Image load() {
// return isCancelled() ? null : file.getThumbnail();
// }
//
// @Override
// void saveToCache(Image result) {
//// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
// }
// }
}

View File

@ -169,21 +169,23 @@ public class MetaDataPane extends DrawableUIBase {
@Override
synchronized protected void setFileHelper(Long newFileID) {
setFileIDOpt(Optional.ofNullable(newFileID));
if (newFileID == null) {
Platform.runLater(() -> {
imageView.setImage(null);
imageBorder.setCenter(null);
tableView.getItems().clear();
getCategoryBorderRegion().setBorder(null);
});
} else {
disposeContent();
disposeContent();
if (nonNull(newFileID)) {
updateAttributesTable();
updateCategory();
updateContent();
}
}
@Override
protected synchronized void disposeContent() {
super.disposeContent();
Platform.runLater(() -> {
tableView.getItems().clear();
getCategoryBorderRegion().setBorder(null);
});
}
@Override
Task<Image> newReadImageTask(DrawableFile<?> file) {
return file.getThumbnailTask();

View File

@ -19,8 +19,6 @@
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
import java.io.IOException;
import java.lang.ref.SoftReference;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
@ -76,6 +74,8 @@ public class SlideShowView extends DrawableTileBase {
@FXML
private BorderPane footer;
private volatile MediaLoadTask mediaTask;
SlideShowView(GroupPane gp, ImageGalleryController controller) {
super(gp, controller);
FXMLConstructor.construct(this, "SlideShowView.fxml");
@ -160,49 +160,58 @@ public class SlideShowView extends DrawableTileBase {
}
@Override
protected void disposeContent() {
synchronized protected void disposeContent() {
stopVideo();
if (mediaTask != null) {
mediaTask.cancel(true);
}
mediaTask = null;
super.disposeContent();
// if (mediaTask != null) {
// mediaTask.cancel(true);
// }
// mediaTask = null;
mediaCache = null;
}
private SoftReference<Node> mediaCache;
@Override
synchronized protected void updateContent() {
if (getFile().isPresent() == false) {
mediaCache = null;
Platform.runLater(() -> imageBorder.setCenter(null));
} else {
disposeContent();
if (getFile().isPresent()) {
DrawableFile<?> file = getFile().get();
if (file.isVideo()) {
//specially handling for videos
Node mediaNode = (isNull(mediaCache)) ? null : mediaCache.get();
if (nonNull(mediaNode)) {
Platform.runLater(() -> imageBorder.setCenter(mediaNode));
} else {
MediaLoadTask mediaTask = new MediaLoadTask(((VideoFile<?>) file));
Node progressNode = newProgressIndicator(mediaTask);
Platform.runLater(() -> imageBorder.setCenter(progressNode));
//called on fx thread
mediaTask.setOnSucceeded(succedded -> showMedia(file, mediaTask));
mediaTask.setOnFailed(failed -> showErrorNode(getMediaLoadErrorLabel(mediaTask), file));
exec.execute(mediaTask);
}
doMediaLoadTask((VideoFile<?>) file);
} else {
super.updateContent();
doReadImageTask(file);
}
}
}
synchronized private Node doMediaLoadTask(VideoFile<?> file) {
//specially handling for videos
MediaLoadTask myTask = new MediaLoadTask(file);
mediaTask = myTask;
Node progressNode = newProgressIndicator(myTask);
Platform.runLater(() -> imageBorder.setCenter(progressNode));
//called on fx thread
mediaTask.setOnSucceeded(succeedded -> {
showMedia(file, myTask);
synchronized (SlideShowView.this) {
mediaTask = null;
}
});
mediaTask.setOnFailed(failed -> {
showErrorNode(getMediaLoadErrorLabel(myTask), file);
synchronized (SlideShowView.this) {
mediaTask = null;
}
});
mediaTask.setOnCancelled(cancelled -> {
disposeContent();
});
exec.execute(myTask);
return progressNode;
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private void showMedia(DrawableFile<?> file, Task<Node> mediaTask) {
//Note that all error conditions are allready logged in readImageTask.succeeded()