
org.dishevelled.thumbnail.AbstractThumbnailManager Maven / Gradle / Ivy
/*
dsh-thumbnail Implementation of the freedesktop.org thumbnail specification.
Copyright (c) 2013-2019 held jointly by the individual authors.
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 3 of the License, or (at
your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> http://www.fsf.org/licensing/licenses/lgpl.html
> http://www.opensource.org/licenses/lgpl-license.php
*/
package org.dishevelled.thumbnail;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermission;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.apache.commons.codec.digest.DigestUtils;
import org.imgscalr.Scalr;
/**
* Abstract thumbnail manager.
*
* @author Michael Heuer
*/
public abstract class AbstractThumbnailManager implements ThumbnailManager
{
/** Root thumbnail directory. */
private final File directory;
/** Directory for normal size (128x128 pixel) thumbnails. */
private final File normalDirectory;
/** Directory for large size (256x256 pixel) thumbnails. */
private final File largeDirectory;
/** Directory for metadata about failed thumbnail images. */
private final File failDirectory;
/** Maximum cache size. */
private static final int MAXIMUM_SIZE = 1000;
/** Cache expire after write, in seconds. */
private static final int EXPIRE = 30;
/** Cache of thumbnails keyed by thumbnail file. */
private final LoadingCache cache = CacheBuilder.newBuilder()
.maximumSize(MAXIMUM_SIZE)
.expireAfterWrite(EXPIRE, TimeUnit.SECONDS)
.build(new CacheLoader()
{
@Override
public Thumbnail load(final File key) throws IOException
{
return Thumbnail.read(key);
}
});
/** Normal (128x128 pixel) size. */
private static final int NORMAL_SIZE = 128;
/** Large (256x256 pixel) size. */
private static final int LARGE_SIZE = 256;
/** Directory permissions. */
private static final Set DIRECTORY_PERMISSIONS = EnumSet.of(PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.OWNER_EXECUTE);
/** File permissions. */
private static final Set FILE_PERMISSIONS = EnumSet.of(PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE);
/**
* Create a new abstract thumbnail manager with the specified root thumbnail directory.
*
* @param directory root thumbnail directory, must not be null
*/
protected AbstractThumbnailManager(final File directory)
{
if (directory == null)
{
throw new IllegalArgumentException("directory must not be null");
}
this.directory = directory;
normalDirectory = new File(this.directory, "normal");
largeDirectory = new File(this.directory, "large");
// todo: fail directory should be application specific; e.g. fail/dsh-thumbnail-1.0/
failDirectory = new File(this.directory, "fail");
normalDirectory.mkdirs();
largeDirectory.mkdirs();
failDirectory.mkdirs();
fixDirectoryPermissions(this.directory);
fixDirectoryPermissions(normalDirectory);
fixDirectoryPermissions(largeDirectory);
fixDirectoryPermissions(failDirectory);
}
/**
* Fix the permissions of the specified file.
*
* @param file file
*/
private void fixPermissions(final File file)
{
try
{
// set to 600
Files.setPosixFilePermissions(file.toPath(), FILE_PERMISSIONS);
}
catch (Exception e)
{
// ignore
}
}
/**
* Fix the permissions of the specified directory.
*
* @param directory directory
*/
private void fixDirectoryPermissions(final File directory)
{
try
{
// set to 700
Files.setPosixFilePermissions(directory.toPath(), DIRECTORY_PERMISSIONS);
}
catch (Exception e)
{
// ignore
}
}
/**
* Attempt to create an empty fail file.
*
* @param failFile fail file to create
*/
private void createFailFile(final File failFile)
{
try
{
failFile.createNewFile();
}
catch (IOException e)
{
// ignore
}
}
/**
* Create and return a thumbnail image for the specified URI.
*
* @param uri URI for the original image, must not be null
* @param modificationTime modification time for the original image
* @param thumbnailDirectory thumbnail directory
* @param size size
* @return a thumbnail image for the specified URI
* @throws IOException if an I/O error occurs
*/
private BufferedImage createThumbnail(final URI uri,
final long modificationTime,
final File thumbnailDirectory,
final int size) throws IOException
{
if (uri == null)
{
throw new IllegalArgumentException("uri must not be null");
}
String fileName = DigestUtils.md5Hex(uri.toString()) + ".png";
// fail fast if fail file exists
File failFile = new File(failDirectory, fileName);
if (failFile.exists())
{
throw new IOException("cannot create a thumbnail image for " + uri);
}
// check if thumbnail exists
File thumbnailFile = new File(thumbnailDirectory, fileName);
if (thumbnailFile.exists())
{
Thumbnail thumbnail = cache.getUnchecked(thumbnailFile);
if (thumbnail.getModificationTime() == modificationTime)
{
return thumbnail.getImage();
}
}
// load the image and create new thumbnail
URL url = uri.toURL();
BufferedImage image = null;
try
{
image = ImageIO.read(url);
}
catch (IOException e)
{
createFailFile(failFile);
throw new IOException("cannot create a thumbnail image for " + uri, e);
}
if (image == null)
{
createFailFile(failFile);
throw new IOException("cannot create a thumbnail image for " + uri);
}
Thumbnail thumbnail = new Thumbnail(uri, modificationTime, image.getWidth(), image.getHeight(),
Scalr.resize(image, size));
File tmp = File.createTempFile("tmp", ".png", thumbnailDirectory);
fixPermissions(tmp);
thumbnail.write(tmp);
com.google.common.io.Files.move(tmp, thumbnailFile);
return thumbnail.getImage();
}
@Override
public final BufferedImage createThumbnail(final URI uri, final long modificationTime) throws IOException
{
return createThumbnail(uri, modificationTime, normalDirectory, NORMAL_SIZE);
}
@Override
public final BufferedImage createLargeThumbnail(final URI uri, final long modificationTime) throws IOException
{
return createThumbnail(uri, modificationTime, largeDirectory, LARGE_SIZE);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy