architecture.ee.web.attachment.DefaultImageManager.java Source code

Java tutorial

Introduction

Here is the source code for architecture.ee.web.attachment.DefaultImageManager.java

Source

/*
 * Copyright 2012, 2013 Donghyuck, Son
 *
 * 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 architecture.ee.web.attachment;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.poi.util.IOUtils;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import architecture.common.lifecycle.ConfigService;
import architecture.common.util.PlatformHelper.Platform;
import architecture.ee.exception.NotFoundException;
import architecture.ee.exception.SystemException;
import architecture.ee.util.ApplicationHelper;
import architecture.ee.web.attachment.dao.ImageDao;
import architecture.ee.web.attachment.dao.ImageLinkDao;
import architecture.ee.web.attachment.impl.ImageImpl;
import net.coobird.thumbnailator.Thumbnails;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

public class DefaultImageManager extends AbstractAttachmentManager implements ImageManager {

    private ConfigService configService;

    private Lock lock = new ReentrantLock();
    private ImageConfig imageConfig;
    private ImageDao imageDao;
    private ImageLinkDao imageLinkDao;

    private Cache imageLinkIdCache;
    private Cache imageLinkCache;
    private Cache imageCache;
    private File imageDir;

    public void setConfigService(ConfigService configService) {
        this.configService = configService;
    }

    /**
     * @return imageLinkIdCache
     */
    public Cache getImageLinkIdCache() {
        return imageLinkIdCache;
    }

    /**
     * @param imageLinkIdCache
     *             imageLinkIdCache
     */
    public void setImageLinkIdCache(Cache imageLinkIdCache) {
        this.imageLinkIdCache = imageLinkIdCache;
    }

    /**
     * @return imageLinkDao
     */
    public ImageLinkDao getImageLinkDao() {
        return imageLinkDao;
    }

    /**
     * @param imageLinkDao
     *             imageLinkDao
     */
    public void setImageLinkDao(ImageLinkDao imageLinkDao) {
        this.imageLinkDao = imageLinkDao;
    }

    /**
     * @return imageLinkCache
     */
    public Cache getImageLinkCache() {
        return imageLinkCache;
    }

    /**
     * @param imageLinkCache
     *             imageLinkCache
     */
    public void setImageLinkCache(Cache imageLinkCache) {
        this.imageLinkCache = imageLinkCache;
    }

    public ImageConfig getImageConfig() {
        return imageConfig;
    }

    public void setImageConfig(ImageConfig imageConfig) {
        this.imageConfig = imageConfig;
    }

    public ImageDao getImageDao() {
        return imageDao;
    }

    public void setImageDao(ImageDao imageDao) {
        this.imageDao = imageDao;
    }

    public boolean isImageEnabled() {
        return imageConfig.isEnabled();
    }

    public void setImageEnabled(boolean enabled) {
        imageConfig.setEnabled(enabled);
        String str = (new StringBuilder()).append("").append(enabled).toString();
        configService.setApplicationProperty("image.enabled", str);
    }

    public int getMaxImageSize() {
        return imageConfig.getMaxImageSize();
    }

    public void setMaxImageSize(int maxImageSize) {
        imageConfig.setMaxImageSize(maxImageSize);
        String str = (new StringBuilder()).append("").append(maxImageSize).toString();
        configService.setApplicationProperty("image.maxImageSize", str);
    }

    public int getImagePreviewMaxSize() {
        return imageConfig.getImagePreviewMaxSize();
    }

    public void setImagePreviewMaxSize(int imagePreviewMaxSize) {
        imageConfig.setImagePreviewMaxSize(imagePreviewMaxSize);
        String str = (new StringBuilder()).append("").append(imagePreviewMaxSize).toString();
        configService.setApplicationProperty("image.imagePreviewMaxSize", str);
    }

    public boolean isForceThumbnailsEnabled() {
        return imageConfig.isForceThumbnailsEnabled();
    }

    public void setFourceThumbnailsEnabled(boolean forceThumbnailsEnabled) {
        imageConfig.setForceThumbnailsEnabled(forceThumbnailsEnabled);
        String str = (new StringBuilder()).append("").append(forceThumbnailsEnabled).toString();
        configService.setApplicationProperty("image.forceThumbnailsEnabled", str);
    }

    public int getImageMaxWidth() {
        return imageConfig.getImageMaxWidth();
    }

    public void setImageMaxWidth(int imageMaxWidth) {
        imageConfig.setImageMaxWidth(imageMaxWidth);
        String str = (new StringBuilder()).append("").append(imageMaxWidth).toString();
        configService.setApplicationProperty("image.imageMaxWidth", str);
    }

    public int getImageMaxHeight() {
        return imageConfig.getImageMaxHeight();
    }

    public void setImageMaxHeight(int imageMaxHeight) {
        imageConfig.setImageMaxHeight(imageMaxHeight);
        String str = (new StringBuilder()).append("").append(imageMaxHeight).toString();
        configService.setApplicationProperty("image.imageMaxHeight", str);
    }

    public boolean isValidType(String contentType) {
        boolean flag;
        if (isAllowAllByDefault())
            flag = !getDisallowedTypes().contains(contentType);
        else
            flag = getAllowedTypes().contains(contentType);
        return flag;
    }

    public List<String> getDisallowedTypes() {
        return imageConfig.getDisallowedTypes();
    }

    public void addDisallowedType(String contentType) {
        if (!imageConfig.getDisallowedTypes().contains(contentType)) {
            imageConfig.getDisallowedTypes().add(contentType);
            String str = listToString(imageConfig.getDisallowedTypes());
            configService.setApplicationProperty("image.disallowedTypes", str);
        }
    }

    public void removeDisallowedType(String contentType) {
        if (imageConfig.getDisallowedTypes().contains(contentType)) {
            imageConfig.getDisallowedTypes().remove(contentType);
            String str = listToString(imageConfig.getDisallowedTypes());
            configService.setApplicationProperty("image.disallowedTypes", str);
        }
    }

    public void addAllowedType(String contentType) {
        if (!imageConfig.getAllowedTypes().contains(contentType)) {
            imageConfig.getAllowedTypes().add(contentType);
            String str = listToString(imageConfig.getAllowedTypes());
            configService.setApplicationProperty("image.allowedTypes", str);
        }
    }

    public void removeAllowedType(String contentType) {
        if (imageConfig.getAllowedTypes().contains(contentType)) {
            imageConfig.getAllowedTypes().remove(contentType);
            String str = listToString(imageConfig.getAllowedTypes());
            configService.setApplicationProperty("image.allowedTypes", str);
        }
    }

    public List<String> getAllowedTypes() {
        return imageConfig.getAllowedTypes();
    }

    public boolean isAllowAllByDefault() {
        return imageConfig.isAllowAllByDefault();
    }

    public void setAllowAllByDefault(boolean allowed) {
        imageConfig.setAllowAllByDefault(allowed);
        String str = (new StringBuilder()).append("").append(allowed).toString();
        configService.setApplicationProperty("image.allowAllByDefault", str);
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void deleteImage(Image image) {
        Image imageToUse = imageDao.getImageById(image.getImageId());
        imageDao.deleteImage(imageToUse);
        File dir = new File(getImageDir(), "cache");
        File imageFile = new File(dir,
                (new StringBuilder()).append(imageToUse.getImageId()).append(".bin").toString());
        if (imageFile.exists())
            imageFile.delete();
    }

    public Image createImage(int objectType, long objectId, String name, String contentType, File file) {
        Date now = new Date();
        ImageImpl image = new ImageImpl();
        image.setCreationDate(now);
        image.setModifiedDate(now);
        image.setObjectType(objectType);
        image.setObjectId(objectId);
        image.setContentType(contentType);
        image.setName(name);
        image.setImageId(-1L);

        try {
            image.setSize((int) FileUtils.sizeOf(file));
            image.setInputStream(FileUtils.openInputStream(file));
        } catch (IOException e) {
            log.debug(e);
        }
        return image;
    }

    public Image createImage(int objectType, long objectId, String name, String contentType,
            InputStream inputStream) {
        Date now = new Date();
        ImageImpl image = new ImageImpl();
        image.setCreationDate(now);
        image.setModifiedDate(now);
        image.setObjectType(objectType);
        image.setObjectId(objectId);
        image.setContentType(contentType);
        image.setName(name);
        image.setImageId(-1L);

        try {
            image.setSize(IOUtils.toByteArray(inputStream).length);
            image.setInputStream(inputStream);
        } catch (IOException e) {
            log.debug(e);
        }
        return image;
    }

    public Image createImage(int objectType, long objectId, String name, String contentType,
            InputStream inputStream, int size) {
        Date now = new Date();
        ImageImpl image = new ImageImpl();
        image.setCreationDate(now);
        image.setModifiedDate(now);
        image.setObjectType(objectType);
        image.setObjectId(objectId);
        image.setContentType(contentType);
        image.setName(name);
        image.setImageId(-1L);
        image.setInputStream(inputStream);
        image.setSize(size);
        return image;
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void saveOrUpdate(Image image) {
        try {
            if (image.getImageId() <= 0) {
                imageDao.createImage(image);
                imageDao.saveImageInputStream(image, image.getInputStream());
            } else {
                Date now = new Date();
                ((ImageImpl) image).setModifiedDate(now);
                imageDao.updateImage(image);
                imageDao.saveImageInputStream(image, image.getInputStream());
            }

            Collection<File> files = FileUtils.listFiles(getImageCacheDir(),
                    FileFilterUtils.prefixFileFilter(image.getImageId() + ""), null);
            for (File file : files) {
                FileUtils.forceDelete(file);
            }
            Image imageToUse = getImage(image.getImageId());
            imageCache.remove(imageToUse.getImageId());
        } catch (Exception e) {
            throw new SystemException(e);
        }
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public Image saveImage(Image image) {

        try {
            if (image.getImageId() <= 0) {
                imageDao.createImage(image);
                imageDao.saveImageInputStream(image, image.getInputStream());
            } else {
                Date now = new Date();
                ((ImageImpl) image).setModifiedDate(now);
                imageDao.updateImage(image);
                imageDao.saveImageInputStream(image, image.getInputStream());
            }

            deleteImageFileCache(image);

            Image imageToUse = getImage(image.getImageId());
            imageCache.remove(imageToUse.getImageId());

            return imageToUse;
        } catch (Exception e) {
            throw new SystemException(e);
        }
    }

    private void deleteImageFileCache(Image image) {
        Collection<File> files = FileUtils.listFiles(getImageCacheDir(),
                FileFilterUtils.prefixFileFilter(String.valueOf(image.getImageId())),
                FileFilterUtils.suffixFileFilter(".bin"));
        for (File file : files) {
            log.debug(file.getPath() + ":" + file.isFile());
            try {
                FileUtils.forceDelete(file);
            } catch (IOException e) {
                log.error(e);
            }
        }

    }

    public Image getImage(long imageId) throws NotFoundException {
        Image imageToUse = null;
        if (imageCache.get(imageId) == null) {
            try {
                imageToUse = getImageById(imageId);
                imageCache.put(new Element(imageId, imageToUse));
            } catch (Exception e) {
                String msg = (new StringBuilder()).append("Unable to find image ").append(imageId).toString();
                throw new NotFoundException(msg, e);
            }
        } else {
            imageToUse = (Image) imageCache.get(imageId).getValue();
        }
        return imageToUse;
    }

    private Image getImageById(long imageId) throws NotFoundException {
        try {
            return imageDao.getImageById(imageId);
        } catch (Exception e) {
            String msg = (new StringBuilder()).append("Unable to find image ").append(imageId).toString();
            throw new NotFoundException(msg, e);
        }
    }

    public Cache getImageCache() {
        return imageCache;
    }

    public void setImageCache(Cache imageCache) {
        this.imageCache = imageCache;
    }

    protected synchronized File getImageDir() {
        if (imageDir == null) {
            imageDir = ApplicationHelper.getRepository().getFile("images");
            if (!imageDir.exists()) {
                boolean result = imageDir.mkdir();
                if (!result)
                    log.error((new StringBuilder()).append("Unable to create image directory: '").append(imageDir)
                            .append("'").toString());

                getImageCacheDir(); // new File(imageDir, "cache");
                getImageTempDir();
            } else {
                File dir = getImageTempDir();
                try {
                    FileUtils.cleanDirectory(dir);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return imageDir;
    }

    public InputStream getImageInputStream(Image image) {
        try {
            File file = getImageFromCacheIfExist(image);
            return FileUtils.openInputStream(file);
        } catch (IOException e) {
            throw new SystemException(e);
        }
    }

    public InputStream getImageThumbnailInputStream(Image image, int width, int height) {
        try {
            File file = getThumbnailFromCacheIfExist(image, width, height);
            return FileUtils.openInputStream(file);
        } catch (IOException e) {
            throw new SystemException(e);
        } finally {

        }
    }

    /**
     * 
     * @param image
     * @return
     * @throws IOException
     */
    protected File getImageFromCacheIfExist(Image image) throws IOException {

        File dir = getImageCacheDir();

        StringBuilder sb = new StringBuilder();
        sb.append(image.getImageId()).append(".bin");
        File file = new File(dir, sb.toString());

        if (file.exists()) {
            long size = FileUtils.sizeOf(file);
            if (size != image.getSize()) {
                // size different make cache new one....
                InputStream inputStream = imageDao.getImageInputStream(image);
                FileUtils.copyInputStreamToFile(inputStream, file);
            }
        } else {
            // doesn't exist, make new one ..
            InputStream inputStream = imageDao.getImageInputStream(image);
            FileUtils.copyInputStreamToFile(inputStream, file);
        }

        return file;
    }

    protected File getImageCacheDir() {
        File dir = new File(getImageDir(), "cache");
        if (!dir.exists()) {
            dir.mkdir();
        }
        return dir;
    }

    protected File getImageTempDir() {
        File dir = new File(getImageDir(), "temp");
        if (!dir.exists()) {
            dir.mkdir();
        }
        return dir;
    }

    protected String toThumbnailFilename(Image image, int width, int height) {
        StringBuilder sb = new StringBuilder();
        sb.append(image.getImageId()).append("_").append(width).append("_").append(height).append(".bin");
        return sb.toString();
    }

    protected File getTemeFile() {
        UUID uuid = UUID.randomUUID();
        File tmp = new File(getImageTempDir(), uuid.toString());
        return tmp;
    }

    protected File getThumbnailFromCacheIfExist(Image image, int width, int height) throws IOException {
        try {
            lock.lock();

            log.debug("thumbnail : " + width + " x " + height);
            File dir = getImageCacheDir();
            File file = new File(dir, toThumbnailFilename(image, width, height));
            File originalFile = getImageFromCacheIfExist(image);

            log.debug("orignal image source: " + originalFile.getAbsoluteFile() + ", " + originalFile.length()
                    + " thumbnail:" + file.getAbsoluteFile() + " - " + file.exists());

            if (file.exists()) {
                log.debug(file.length());
                if (file.length() > 0) {
                    image.setThumbnailSize((int) file.length());
                    return file;
                } else {
                }
            }

            /**
             * TIP : ?  Thumbnail ? ???  ??   ?.
             */
            log.debug("create thumbnail : " + file.getAbsolutePath());
            if (Platform.current() == Platform.WINDOWS) {
                File tmp = getTemeFile();
                Thumbnails.of(originalFile).size(width, height).outputFormat("png")
                        .toOutputStream(new FileOutputStream(tmp));
                image.setThumbnailSize((int) tmp.length());
                FileUtils.copyFile(tmp, file);
            } else {
                try {
                    Thumbnails.of(originalFile).allowOverwrite(true).size(width, height).outputFormat("png")
                            .toOutputStream(new FileOutputStream(file));
                } catch (Throwable e) {
                    log.error(e);
                }
                image.setThumbnailSize((int) file.length());
            }

            return file;

        } finally {
            lock.unlock();
        }

    }

    public void destroy() {

    }

    public void initialize() {
        log.debug("initializing image component");
        ImageConfig imageConfigToUse = new ImageConfig();
        imageConfigToUse.setEnabled(configService.getApplicationBooleanProperty("components.image.enabled", true));
        imageConfigToUse.setAllowAllByDefault(
                configService.getApplicationBooleanProperty("components.image.allowAllByDefault", true));
        imageConfigToUse.setForceThumbnailsEnabled(
                configService.getApplicationBooleanProperty("components.image.forceThumbnailsEnabled", true));
        imageConfigToUse
                .setMaxImageSize(configService.getApplicationIntProperty("components.image.maxImageSize", 2048));
        imageConfigToUse.setImagePreviewMaxSize(
                configService.getApplicationIntProperty("components.image.imagePreviewMaxSize", 250));
        imageConfigToUse
                .setImageMaxWidth(configService.getApplicationIntProperty("components.image.imageMaxWidth", 450));
        imageConfigToUse
                .setImageMaxHeight(configService.getApplicationIntProperty("components.image.imageMaxHeight", 600));
        imageConfigToUse.setMaxImagesPerObject(
                configService.getApplicationIntProperty("components.image.maxImagesPerObject", 50));
        imageConfigToUse.setAllowedTypes(
                stringToList(configService.getApplicationProperty("components.image.allowedTypes", "")));
        imageConfigToUse.setDisallowedTypes(
                stringToList(configService.getApplicationProperty("components.image.disallowedTypes", "")));
        this.imageConfig = imageConfigToUse;

        getImageDir();
        log.debug(imageConfig);
    }

    public List<Image> getImages(int objectType, long objectId) {
        List<Long> imageIds = imageDao.getImageIds(objectType, objectId);
        List<Image> list = new ArrayList<Image>(imageIds.size());
        for (Long imageId : imageIds)
            try {
                list.add(getImage(imageId));
            } catch (Exception e) {
            }

        return list;
    }

    public List<Image> getImages(int objectType, long objectId, int startIndex, int maxResults) {
        List<Long> imageIds = imageDao.getImageIds(objectType, objectId, startIndex, maxResults);
        List<Image> list = new ArrayList<Image>(imageIds.size());
        for (Long imageId : imageIds)
            try {
                list.add(getImage(imageId));
            } catch (Exception e) {
            }

        return list;
    }

    public int getTotalImageCount(int objectType, long objectId) {
        return imageDao.getImageCount(objectType, objectId);
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void updateImageProperties(Image image) {
        try {
            if (image.getImageId() > 0) {
                Date now = new Date();
                ((ImageImpl) image).setModifiedDate(now);
                imageDao.updateImage(image);
            }
            imageCache.remove(image.getImageId());
        } catch (Exception e) {
            throw new SystemException(e);
        }
    }

    public Image getImageByImageLink(String linkId) throws NotFoundException {
        Long imageIdToUse = -1L;
        if (imageLinkIdCache.get(linkId) == null) {
            try {
                ImageLink link = imageLinkDao.getImageLink(linkId);
                imageLinkIdCache.put(new Element(link.getLinkId(), link.getImageId()));
                return getImageById(link.getImageId());
            } catch (Exception e) {
                String msg = (new StringBuilder()).append("Unable to find image ").append(linkId).toString();
                throw new NotFoundException(msg, e);
            }
        } else {
            imageIdToUse = (Long) imageLinkIdCache.get(linkId).getValue();
        }
        return getImage(imageIdToUse);
    }

    public ImageLink getImageLink(Image image) throws NotFoundException {
        ImageLink link = null;
        Long imageIdToUse = image.getImageId();
        if (imageLinkCache.get(imageIdToUse) == null) {
            try {
                link = imageLinkDao.getImageLinkByImageId(imageIdToUse);
                imageLinkCache.put(new Element(imageIdToUse, link));
            } catch (Exception e) {
                String msg = (new StringBuilder()).append("Unable to find image link for iamge : ")
                        .append(imageIdToUse).toString();
                throw new NotFoundException(msg, e);
            }
        } else {
            link = (ImageLink) imageLinkCache.get(imageIdToUse).getValue();
        }
        return link;
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public ImageLink getImageLink(Image image, boolean createIfNotExist) throws NotFoundException {
        try {
            return getImageLink(image);
        } catch (NotFoundException e) {
            if (createIfNotExist) {
                ImageLink link = new ImageLink(RandomStringUtils.random(64, true, true), image.getImageId(), true);
                imageLinkDao.saveImageLink(link);
                imageLinkCache.put(new Element(image.getImageId(), link));
                return link;
            } else {
                throw e;
            }
        }
    }

    public void removeImageLink(Image image) {
        try {
            ImageLink link = getImageLink(image);
            imageLinkIdCache.remove(link.getLinkId());
            imageLinkCache.remove(image.getImageId());
            imageLinkDao.removeImageLink(link);
        } catch (NotFoundException e) {
        }
    }

    public long getUsage(int objectType, long objectId) {
        return imageDao.getImageUsage(objectType, objectId);
    }

}