Package ch.entwine.weblounge.contentrepository.impl

Source Code of ch.entwine.weblounge.contentrepository.impl.PreviewGeneratorWorker

/*
*  Weblounge: Web Content Management System
*  Copyright (c) 2012 The Weblounge Team
*  http://weblounge.o2it.ch
*
*  This program 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 2
*  of the License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without 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 program; if not, write to the Free Software Foundation
*  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package ch.entwine.weblounge.contentrepository.impl;

import ch.entwine.weblounge.common.content.PreviewGenerator;
import ch.entwine.weblounge.common.content.Resource;
import ch.entwine.weblounge.common.content.ResourceContent;
import ch.entwine.weblounge.common.content.ResourceURI;
import ch.entwine.weblounge.common.content.ResourceUtils;
import ch.entwine.weblounge.common.content.image.ImagePreviewGenerator;
import ch.entwine.weblounge.common.content.image.ImageStyle;
import ch.entwine.weblounge.common.impl.content.image.ImageStyleUtils;
import ch.entwine.weblounge.common.language.Language;
import ch.entwine.weblounge.common.repository.ContentRepositoryException;
import ch.entwine.weblounge.common.repository.ResourceSerializer;
import ch.entwine.weblounge.common.site.Environment;
import ch.entwine.weblounge.common.site.ImageScalingMode;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

/**
* Worker implementation that creates a preview in a separate thread.
*/
class PreviewGeneratorWorker implements Runnable {

  /** The logging facility */
  private final Logger logger = LoggerFactory.getLogger(PreviewGeneratorWorker.class);

  /** The content repository */
  private AbstractContentRepository contentRepository = null;

  /** The resource to render */
  private Resource<?> resource = null;

  /** The list of image styles to produce */
  private List<ImageStyle> styles = null;

  /** The environment */
  private Environment environment = null;

  /** The languages to render */
  private List<Language> languages = null;

  /** The format */
  private String format = null;

  /** Flag to indicate a canceled preview generation */
  private boolean canceled = false;

  /**
   * Creates a new preview worker who will create the corresponding previews for
   * the given resource and style.
   *
   * @param resource
   *          the resource
   * @param environment
   *          the current environment
   * @param languages
   *          the languages
   * @param styles
   *          the image styles
   */
  public PreviewGeneratorWorker(AbstractContentRepository repository,
      Resource<?> resource, Environment environment, List<Language> languages,
      List<ImageStyle> styles, String format) {
    if (languages == null || languages.size() == 0)
      throw new IllegalArgumentException("At least one language must be provided");
    if (styles == null || styles.size() == 0)
      throw new IllegalArgumentException("At least one preview style must be provided");
    this.contentRepository = repository;
    this.resource = resource;
    this.environment = environment;
    this.languages = languages;
    this.styles = styles;
    this.format = format;
  }

  /**
   * Indicates to this preview generation worker to cancel the current
   * operation.
   */
  public void cancel() {
    this.canceled = true;
  }

  /**
   * {@inheritDoc}
   *
   * @see java.lang.Runnable#run()
   */
  public void run() {
    ResourceURI resourceURI = resource.getURI();
    String resourceType = resourceURI.getType();

    try {

      // Find the resource serializer
      ResourceSerializer<?, ?> serializer = contentRepository.getSerializerByType(resourceType);
      if (serializer == null) {
        logger.warn("Unable to index resources of type '{}': no resource serializer found", resourceType);
        return;
      }

      // Does the serializer come with a preview generator?
      PreviewGenerator previewGenerator = serializer.getPreviewGenerator(resource);
      if (previewGenerator == null) {
        logger.debug("Resource type '{}' does not support previews", resourceType);
        return;
      }

      // Create the scaled images
      String mimeType = "image/" + format;
      ResourceSerializer<?, ?> s = contentRepository.getSerializerByMimeType(mimeType);
      if (s == null) {
        logger.warn("No resource serializer is capable of dealing with resources of format '{}'", mimeType);
        return;
      } else if (!(s instanceof ImageResourceSerializer)) {
        logger.warn("Resource serializer lookup for format '{}' returned {}", format, s.getClass());
        return;
      }

      // Find us an image serializer
      ImageResourceSerializer irs = (ImageResourceSerializer) s;
      ImagePreviewGenerator imagePreviewGenerator = (ImagePreviewGenerator) irs.getPreviewGenerator(format);
      if (imagePreviewGenerator == null) {
        logger.warn("Image resource serializer {} does not provide support for '{}'", irs, format);
        return;
      }

      // Now scale the original preview according to the existing styles
      for (Language l : languages) {
        if (!resource.supportsContentLanguage(l))
          continue;

        // Have we been told to stop doing work in the meantime?
        if (canceled)
          return;

        // Create the original preview image for every language
        File originalPreview = null;
        if (!resource.supportsContentLanguage(l))
          continue;
        originalPreview = createPreview(resource, null, l, previewGenerator, format);
        if (originalPreview == null || !originalPreview.exists() || originalPreview.length() == 0) {
          logger.warn("Preview generation for {} failed", resource);
          return;
        }

        long resourceLastModified = ResourceUtils.getModificationDate(resource, l).getTime();

        // Create the remaining styles
        for (ImageStyle style : styles) {

          // Have we been told to stop doing work in the meantime?
          if (canceled)
            return;

          // The original has been produced already
          if (ImageScalingMode.None.equals(style.getScalingMode()))
            continue;

          FileInputStream fis = null;
          FileOutputStream fos = null;
          try {
            File scaledFile = ImageStyleUtils.createScaledFile(resource, l, style);

            // Create the file if it doesn't exist or if it is out dated. Note
            // that the last modified date of a file has a precision of seconds
            if (!scaledFile.isFile() || FileUtils.isFileOlder(scaledFile, new Date(resourceLastModified))) {

              logger.info("Creating preview at {}", scaledFile.getAbsolutePath());

              fis = new FileInputStream(originalPreview);
              fos = new FileOutputStream(scaledFile);
              imagePreviewGenerator.createPreview(originalPreview, environment, l, style, format, fis, fos);
              scaledFile.setLastModified(Math.max(new Date().getTime(), resourceLastModified));

              // Store the style definition used while creating the preview
              File baseDir = ImageStyleUtils.getDirectory(resource.getURI().getSite(), style);
              File definitionFile = new File(baseDir, "style.xml");
              if (!definitionFile.isFile()) {
                logger.debug("Storing style definition at {}", definitionFile);
                definitionFile.createNewFile();
                FileUtils.copyInputStreamToFile(IOUtils.toInputStream(style.toXml(), "UTF-8"), definitionFile);
              }
            } else {
              logger.debug("Skipping creation of existing '{}' preview of {}", style, resource);
            }

          } catch (Throwable t) {
            logger.error("Error scaling {}: {}", originalPreview, t.getMessage());
            continue;
          } finally {
            IOUtils.closeQuietly(fis);
            IOUtils.closeQuietly(fos);
          }
        }
      }

    } finally {
      if (canceled)
        logger.debug("Preview operation for {} has been canceled", resource.getIdentifier());
      contentRepository.previewCreated(resource);
    }
  }

  /**
   * Creates the actual preview.
   *
   * @param resource
   *          the resource
   * @param style
   *          the image style
   * @param language
   *          the language
   * @param previewGenerator
   *          the preview generator
   * @param the
   *          preview format
   * @return returns the preview file
   */
  private File createPreview(Resource<?> resource, ImageStyle style,
      Language language, PreviewGenerator previewGenerator, String format) {

    ResourceURI resourceURI = resource.getURI();
    String resourceType = resourceURI.getType();

    // Create the filename
    ResourceContent content = resource.getContent(language);

    // Initiate creation of previews
    InputStream resourceInputStream = null;
    InputStream contentRepositoryIs = null;
    FileOutputStream fos = null;
    File scaledResourceFile = null;

    try {
      scaledResourceFile = ImageStyleUtils.getScaledFile(resource, language, style);

      // Find the modification date
      long lastModified = ResourceUtils.getModificationDate(resource, language).getTime();

      // Create the file if it doesn't exist or if it is out dated. Note that
      // the last modified date of a file has a precision of seconds
      if (!scaledResourceFile.isFile() || FileUtils.isFileOlder(scaledResourceFile, new Date(lastModified))) {
        contentRepositoryIs = contentRepository.getContent(resourceURI, language);

        // Is this local content?
        if (contentRepositoryIs == null && content != null && content.getExternalLocation() != null) {
          contentRepositoryIs = content.getExternalLocation().openStream();
        }

        // Create the parent directory
        File scaledResourceDir = scaledResourceFile.getParentFile();
        if (!scaledResourceDir.isDirectory() && !scaledResourceDir.mkdirs()) {
          AbstractContentRepository.logger.warn("Error creating parent directory of preview file {}", scaledResourceFile.getAbsolutePath());
          return null;
        }

        // Create the file if it doesn't exist
        if (!scaledResourceFile.isFile() && !scaledResourceFile.createNewFile()) {
          AbstractContentRepository.logger.warn("Error creating preview file {}", scaledResourceFile.getAbsolutePath());
          return null;
        }

        // Create the preview
        fos = new FileOutputStream(scaledResourceFile);
        AbstractContentRepository.logger.debug("Creating preview of '{}' at {}", resource, scaledResourceFile);
        previewGenerator.createPreview(resource, environment, language, style, format, contentRepositoryIs, fos);
      }

    } catch (ContentRepositoryException e) {
      AbstractContentRepository.logger.error("Error loading {} {} '{}' from {}: {}", new Object[] {
          language,
          resourceType,
          resource,
          this,
          e.getMessage() });
      AbstractContentRepository.logger.error(e.getMessage(), e);
      IOUtils.closeQuietly(resourceInputStream);
    } catch (IOException e) {
      AbstractContentRepository.logger.warn("Error creating preview for {} '{}': {}", new Object[] {
          resourceType,
          resourceURI,
          e.getMessage() });
      IOUtils.closeQuietly(resourceInputStream);
    } catch (Throwable t) {
      AbstractContentRepository.logger.warn("Error creating preview for {} '{}': {}", new Object[] {
          resourceType,
          resourceURI,
          t.getMessage() });
      IOUtils.closeQuietly(resourceInputStream);

    } finally {
      IOUtils.closeQuietly(contentRepositoryIs);
      IOUtils.closeQuietly(fos);

      // Make sure corrupted preview images are being deleted
      File f = scaledResourceFile;
      if (f != null && f.length() == 0) {
        FileUtils.deleteQuietly(f);
        f = f.getParentFile();
        while (f != null && f.isDirectory() && (f.listFiles() == null || f.listFiles().length == 0)) {
          FileUtils.deleteQuietly(f);
          f = f.getParentFile();
        }
      }
    }

    return scaledResourceFile;
  }

}
TOP

Related Classes of ch.entwine.weblounge.contentrepository.impl.PreviewGeneratorWorker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.