Package org.apache.clerezza.platform.content.representations.core

Source Code of org.apache.clerezza.platform.content.representations.core.ThumbnailService

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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 org.apache.clerezza.platform.content.representations.core;

import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.clerezza.jaxrs.utils.RedirectUtil;
import org.apache.clerezza.platform.config.PlatformConfig;
import org.apache.clerezza.platform.graphprovider.content.ContentGraphProvider;
import org.apache.clerezza.rdf.core.Literal;
import org.apache.clerezza.rdf.core.LiteralFactory;
import org.apache.clerezza.rdf.core.Resource;
import org.apache.clerezza.rdf.core.TypedLiteral;
import org.apache.clerezza.rdf.core.UriRef;
import org.apache.clerezza.rdf.ontologies.DISCOBITS;
import org.apache.clerezza.rdf.ontologies.EXIF;
import org.apache.clerezza.rdf.utils.GraphNode;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.scr.annotations.Services;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This JAX-RS resource provides a method to retrieve the uri to
* the thumbnail or a other small representation of a InfoDiscoBit.
*
* @author mir
*/
@Component
@Services({
  @Service(Object.class),
  @Service(ThumbnailService.class)
})

@Property(name = "javax.ws.rs", boolValue = true)
@Path("thumbnail-service")
public class ThumbnailService implements BundleListener {

  @Reference
  ContentGraphProvider cgProvider;
  @Reference
  PlatformConfig config;
  @Reference
  AlternativeRepresentationGenerator altRepGen;
  private static final Logger log = LoggerFactory.getLogger(ThumbnailService.class);
  private BundleContext bundleContext;
  private String STATICWEB_PATH = "/CLEREZZA-INF/web-resources/style/";
  private String MEDIA_TYPE_BASE_PATH = STATICWEB_PATH + "images/icons/mediatype/";
  private Bundle cachedStyleBundle = null;
  private Map<MediaType, String> mediaTypeIconUriCache =
      Collections.synchronizedMap(new HashMap<MediaType, String>());

  protected void activate(ComponentContext context) {
    bundleContext = context.getBundleContext();
    bundleContext.addBundleListener(this);
  }

  protected void deactivate(ComponentContext context) {
    bundleContext.removeBundleListener(this);
    bundleContext = null;
    mediaTypeIconUriCache.clear();
  }

  /**
   * Returns the thumbnail uri for a InfoDiscoBit which is located at the uri
   * specified over the query parameter "uri". The thumbnails
   * maximum width and height can optionally be specified over the query parameters
   * "width" and "height". Futhermore there is the optional "exact" parameter,
   * which specifies if the thumbnail must have the exact width and height as
   * specified or not (default is "false"). If more than one acceptable thumbnail
   * is available then the thumbnail uri of the thumbnail with the highest resolution
   * (width * height) is returned. If no thumbnail is available and the logged
   * in user has the write permission for the content graph, then an attempt is
   * made to create the thumbnail on the fly. If this fails or the write permission
   * is missing, then the uri of the icon representing the media type is returned.
   * If also no media type icon is available the uri to default icon is returned.
   *
   * @param infoBitUri the uri of the infoDiscoBit of which the thumbnail uri should
   *    be returned
   * @param height the maximum height that the thumbnail has
   * @param width the maximum width that the thumbnail has
   * @param exact boolean that specifies if the return thumbnail should have
   *    the exact width and height.
   * @return
   */
  @GET
  public Response getThumbnailUri(@QueryParam("uri") UriRef infoBitUri,
      @QueryParam("width") Integer width, @QueryParam("height") Integer height,
      @DefaultValue("false") @QueryParam("exact") Boolean exact, @Context UriInfo uriInfo) {
    return RedirectUtil.createSeeOtherResponse(
        getThumbnailUri(infoBitUri, width, height, exact).getUnicodeString(), uriInfo);
  }


  /**
   * Returns the thumbnail uri for a InfoDiscoBit which is located at the uri
   * specified over the query parameter "uri". The thumbnails
   * maximum width and height can optionally be specified over the query parameters
   * "width" and "height". If more than one acceptable thumbnail is available
   * then the thumbnail uri of the thumbnail with the highest resolution
   * (width * height) is returned. If no thumbnail is available and the logged
   * in user has the write permission for the content graph, then an attempt is
   * made to create the thumbnail on the fly. If this fails or the write permission
   * is missing, then the uri of the icon representing the media type is returned.
   * If also no media type icon is available the uri to default icon is returned.
   *
   * @param infoBitUri the uri of the infoDiscoBit of which the thumbnail uri should
   *    be returned
   * @param height the maximum height that the thumbnail has
   * @param width the maximum width that the thumbnail has
   * @return
   */
  public UriRef getThumbnailUri(UriRef infoBitUri, Integer width,  Integer height) {
    return getThumbnailUri(infoBitUri, width, height, false);
  }

  /**
   * Returns the thumbnail uri for a InfoDiscoBit which is located at the uri
   * specified over the query parameter "uri". The thumbnails
   * maximum width and height can optionally be specified over the query parameters
   * "width" and "height". Futhermore there is the optional "exact" parameter,
   * which specifies if the thumbnail must have the exact width and height as
   * specified or not. If more than one acceptable thumbnail is available then
   * the thumbnail uri of the thumbnail with the highest resolution (width * height)
   * is returned. If no thumbnail is available and the logged in user has the write
   * permission for the content graph, then an attempt is made to create the
   * thumbnail on the fly. If this fails or the write permission is missing, then
   * the uri of the icon representing the media type is returned. If also no
   * media type icon is available the uri to default icon is returned.
   *
   * @param infoBitUri the uri of the infoDiscoBit of which the thumbnail uri should
   *    be returned
   * @param height the maximum height that the thumbnail has
   * @param width the maximum width that the thumbnail has
   * @param exact boolean that specifies if the return thumbnail should have
   *    the exact width and height.
   * @return
   */
  public UriRef getThumbnailUri(UriRef infoBitUri, Integer width,  Integer height,
      boolean exact) {
    if ((width == null) && (height == null)) {
      throw new IllegalArgumentException("height and/or width must be specified");
    }
    if (width == null) {
      width = Integer.MAX_VALUE;
    }
    if (height == null) {
      height = Integer.MAX_VALUE;
    }
    GraphNode infoBitNode = new GraphNode(infoBitUri, cgProvider.getContentGraph());
    UriRef thumbnailUri = getGeneratedThumbnailUri(infoBitNode, width, height, exact);
    if (thumbnailUri == null) {
      Literal mediaTypeLiteral = null;
      Lock readLock = infoBitNode.readLock();
      readLock.lock();
      try {
        Iterator<Resource> mediaTypes = infoBitNode.getObjects(DISCOBITS.mediaType);
        if (mediaTypes.hasNext()) {
          mediaTypeLiteral = (Literal) mediaTypes.next();
        }
      } finally {
        readLock.unlock();
      }
      if (mediaTypeLiteral != null) {
        MediaType mediaType = MediaType.valueOf(mediaTypeLiteral.getLexicalForm());
        // if the infoBit is an image, create a thumbnail on the fly.
        if (mediaType.getType().startsWith("image")) {
          try {
            thumbnailUri = altRepGen.generateAlternativeImage(infoBitNode, width,
                height, exact);
          } catch (Exception ex) {
            ex.printStackTrace();
            // Was worth a try. eLets go on
          }
        }
        if (thumbnailUri == null) {
          String iconUri = mediaTypeIconUriCache.get(mediaType);
          if (iconUri == null) {
            iconUri = getMediaTypeIconUri(mediaType);
            mediaTypeIconUriCache.put(mediaType, iconUri);
          }
          thumbnailUri = new UriRef(iconUri);
        }
      }
    }
    if (thumbnailUri == null) {
      thumbnailUri = new UriRef(getDefaultIconUrl(getStyleBundle()));
    }
    return thumbnailUri;
  }

  private String getMediaTypeIconUri(MediaType mediaType) {
    Bundle styleBundle = getStyleBundle();
    if (styleBundle == null) {
      throw new RuntimeException("no style bundle found");
    }
    String path = MEDIA_TYPE_BASE_PATH + mediaType.getType() + "/";
    Enumeration entries = styleBundle.findEntries(path,
        mediaType.getSubtype() + ".*", false);
    String iconUri = createIconUri(entries);
    if (iconUri != null) {
      return iconUri;
    }
    entries = styleBundle.findEntries(path, "any.*", false);
    iconUri = createIconUri(entries);
    if (iconUri != null) {
      return iconUri;
    }
    return getDefaultIconUrl(styleBundle);
  }

  private String getDefaultIconUrl(Bundle bundle) {
    Enumeration entries = bundle.findEntries(MEDIA_TYPE_BASE_PATH, "any.*", false);
    String iconUri = createIconUri(entries);
    if (iconUri != null) {
      return iconUri;
    } else {
      throw new RuntimeException("No default icon found");
    }
  }

  private String createIconUri(Enumeration entries) {
    if (entries != null && entries.hasMoreElements()) {
      URL iconUrl = (URL) entries.nextElement();
      return iconUrl.getPath().replace(STATICWEB_PATH, "style/");
    }
    return null;
  }

  private UriRef getGeneratedThumbnailUri(GraphNode infoBitNode,
      Integer width, Integer height, boolean exact) {
    if (isFittingImage(infoBitNode, width, height, exact)) {
      return (UriRef) infoBitNode.getNode();
    }
    UriRef resultThumbnailUri = null;
    int pixels = 0;
    Lock readLock = infoBitNode.readLock();
    readLock.lock();
    try {
      Iterator<Resource> thumbnails = infoBitNode.getObjects(DISCOBITS.thumbnail);
      while (thumbnails.hasNext()) {
        UriRef thumbnailUri = (UriRef) thumbnails.next();
        GraphNode thumbnailNode = new GraphNode(thumbnailUri,
            cgProvider.getContentGraph());
        int thumbnailPixels = getSurfaceSizeIfFitting(thumbnailNode, width, height, exact);
        if (thumbnailPixels > pixels) {
          if (exact) {
            return thumbnailUri;
          }
          resultThumbnailUri = thumbnailUri;
          pixels = thumbnailPixels;
        }
      }
    } finally {
      readLock.unlock();
    }
    return resultThumbnailUri;
  }

  /**
   * returns the surface in pixel if the image fits withing width and height,
   * or -1 if it doesn't fit
   */
  private int getSurfaceSizeIfFitting(GraphNode infoBitNode, Integer width, Integer height,
      boolean exact) {
    Resource imageRes = infoBitNode.getNode();
    if (imageRes instanceof UriRef) {
      String imageUri = ((UriRef)imageRes).getUnicodeString();
      if (!exact && imageUri.contains(AlternativeRepresentationGenerator.EXACT_APPENDIX)) {
        return -1;
      }
    }   
    Iterator<Resource> exifWidths = infoBitNode.getObjects(EXIF.width);
    Iterator<Resource> exifHeights = infoBitNode.getObjects(EXIF.height);
    if (!exifWidths.hasNext() || !exifHeights.hasNext()) {
      log.warn(infoBitNode.getNode() + " doesn't have exif:width and exif:height");
      return -1;
    }
    int thumbnailWidth = LiteralFactory.getInstance().createObject(
        Integer.class, (TypedLiteral) exifWidths.next());
    int thumbnailHeight = LiteralFactory.getInstance().createObject(
        Integer.class, (TypedLiteral) exifHeights.next());
    if (exact) {
      if (thumbnailHeight == height && thumbnailWidth == width) {
        return 1;
      }
    } else {
      if (thumbnailHeight <= height && thumbnailWidth <= width) {
        return thumbnailWidth * thumbnailHeight;
      }
    }
    return -1;
  }

  /**
   * returns true if infoBitNode is an image and fits
   */
  private boolean isFittingImage(GraphNode infoBitNode, Integer width, Integer height,
      boolean exact) {
    Lock readLock = infoBitNode.readLock();
    readLock.lock();
    try {
      final Iterator<Literal> mediaTypesIter = infoBitNode.getLiterals(DISCOBITS.mediaType);
      if (!mediaTypesIter.hasNext()) {
        return false;
      }
      if (mediaTypesIter.next().getLexicalForm().startsWith("image")) {
        return getSurfaceSizeIfFitting(infoBitNode, width, height, exact) > -1;
      } else {
        return false;
      }
    } finally {
      readLock.unlock();
    }
  }

  private synchronized Bundle getStyleBundle() {
    if (cachedStyleBundle != null) {
      return cachedStyleBundle;
    }
    Bundle[] bundles = bundleContext.getBundles();
    for (Bundle bundle : bundles) {
      URL staticWebPathURL = bundle.getEntry(STATICWEB_PATH);
      if (staticWebPathURL != null) {
        cachedStyleBundle = bundle;
        return bundle;
      }
    }
    return null;
  }

  @Override
  public synchronized void bundleChanged(BundleEvent be) {
    if (be.getType() == BundleEvent.UNINSTALLED
        && be.getBundle().equals(cachedStyleBundle)) {
      cachedStyleBundle = null;
      cachedStyleBundle = getStyleBundle();
      mediaTypeIconUriCache.clear();
    }
  }
}
TOP

Related Classes of org.apache.clerezza.platform.content.representations.core.ThumbnailService

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.