/*
* Copyright (C) 2003-2007 eXo Platform SAS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either version 3
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
package org.exoplatform.wcm.connector.collaboration;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import javax.activation.MimetypesFileTypeMap;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.Session;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import org.exoplatform.container.component.ComponentPlugin;
import org.exoplatform.ecm.utils.text.Text;
import org.exoplatform.services.cms.link.LinkManager;
import org.exoplatform.services.cms.link.NodeFinder;
import org.exoplatform.services.cms.thumbnail.ThumbnailPlugin;
import org.exoplatform.services.cms.thumbnail.ThumbnailService;
import org.exoplatform.services.cms.thumbnail.impl.ThumbnailUtils;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.ext.app.SessionProviderService;
import org.exoplatform.services.jcr.ext.common.SessionProvider;
import org.exoplatform.services.jcr.impl.core.NodeImpl;
import org.exoplatform.services.rest.resource.ResourceContainer;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.wcm.connector.viewer.PDFViewerRESTService;
/**
* Returns a responding data as a thumbnail image.
* {{{{repoName}}}}: The name of repository.
* {{{{workspaceName}}}}: The name of workspace.
* {{{{nodePath}}}}: The node path.
*
* {{{{portalname}}}}: The name of the portal.
* {{{{restcontextname}}}}: The context name of REST web application which is deployed to the "{{{{portalname}}}}" portal.
*
* @LevelAPI Provisional
* @anchor ThumbnailRESTService
*/
@Path("/thumbnailImage/")
public class ThumbnailRESTService implements ResourceContainer {
/** The log **/
private static final Log LOG = ExoLogger.getLogger(ThumbnailRESTService.class.getName());
/** The Constant LAST_MODIFIED_PROPERTY. */
private static final String LAST_MODIFIED_PROPERTY = "Last-Modified";
/** The Constant IF_MODIFIED_SINCE_DATE_FORMAT. */
private static final String IF_MODIFIED_SINCE_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";
private final RepositoryService repositoryService_;
private final ThumbnailService thumbnailService_;
private final NodeFinder nodeFinder_;
private final LinkManager linkManager_;
public ThumbnailRESTService(RepositoryService repositoryService,
ThumbnailService thumbnailService,
NodeFinder nodeFinder,
LinkManager linkManager) {
repositoryService_ = repositoryService;
thumbnailService_ = thumbnailService;
nodeFinder_ = nodeFinder;
linkManager_ = linkManager;
}
/**
* Returns an image with a medium size (64x64).
* For example: /portal/rest/thumbnailImage/medium/repository/collaboration/test.gif/
*
* @param repoName The repository name.
* @param workspaceName The workspace name.
* @param nodePath The node path.
* @return Response inputstream.
* @throws Exception
*
* @anchor ThumbnailRESTService.getThumbnailImage
*/
@Path("/medium/{repoName}/{workspaceName}/{nodePath:.*}/")
@GET
public Response getThumbnailImage(@PathParam("repoName") String repoName,
@PathParam("workspaceName") String workspaceName,
@PathParam("nodePath") String nodePath,
@HeaderParam("If-Modified-Since") String ifModifiedSince) throws Exception {
return getThumbnailByType(workspaceName,
nodePath,
ThumbnailService.MEDIUM_SIZE,
ifModifiedSince);
}
/**
* Returns an image with a big size.
*
* @param repoName The repository name.
* @param workspaceName The workspace name.
* @param nodePath The node path.
* @return Response inputstream.
* @throws Exception
*
* @anchor ThumbnailRESTService.getCoverImage
*/
@Path("/big/{repoName}/{workspaceName}/{nodePath:.*}/")
@GET
public Response getCoverImage(@PathParam("repoName") String repoName,
@PathParam("workspaceName") String workspaceName,
@PathParam("nodePath") String nodePath,
@HeaderParam("If-Modified-Since") String ifModifiedSince) throws Exception {
return getThumbnailByType(workspaceName, nodePath, ThumbnailService.BIG_SIZE, ifModifiedSince);
}
/**
* Returns an image with a large size (300x300).
*
* @param repoName The repository name.
* @param workspaceName The workspace name.
* @param nodePath The node path.
* @return Response inputstream.
* @throws Exception
*
* @anchor ThumbnailRESTService.getLargeImage
*/
@Path("/large/{repoName}/{workspaceName}/{nodePath:.*}/")
@GET
public Response getLargeImage(@PathParam("repoName") String repoName,
@PathParam("workspaceName") String workspaceName,
@PathParam("nodePath") String nodePath,
@HeaderParam("If-Modified-Since") String ifModifiedSince) throws Exception {
return getThumbnailByType(workspaceName, nodePath, ThumbnailService.BIG_SIZE, ifModifiedSince);
}
/**
* Returns an image with a small size (32x32).
*
* @param repoName The repository name.
* @param workspaceName The workspace name.
* @param nodePath The node path.
* @return Response inputstream.
* @throws Exception
*
* @anchor ThumbnailRESTService.getSmallImage
*/
@Path("/small/{repoName}/{workspaceName}/{nodePath:.*}/")
@GET
public Response getSmallImage(@PathParam("repoName") String repoName,
@PathParam("workspaceName") String workspaceName,
@PathParam("nodePath") String nodePath,
@HeaderParam("If-Modified-Since") String ifModifiedSince) throws Exception {
return getThumbnailByType(workspaceName, nodePath, ThumbnailService.SMALL_SIZE, ifModifiedSince);
}
/**
* Returns an image with a custom size.
*
* @param size The customized size of the image.
* @param repoName The repository name.
* @param workspaceName The workspace name.
* @param nodePath The node path.
* @return Response inputstream.
* @throws Exception
*
* @anchor ThumbnailRESTService.getCustomImage
*/
@Path("/custom/{size}/{repoName}/{workspaceName}/{nodePath:.*}/")
@GET
public Response getCustomImage(@PathParam("size") String size,
@PathParam("repoName") String repoName,
@PathParam("workspaceName") String workspaceName,
@PathParam("nodePath") String nodePath,
@HeaderParam("If-Modified-Since") String ifModifiedSince) throws Exception {
return getThumbnailByType(workspaceName, nodePath, "exo:"+size, ifModifiedSince);
}
/**
* Returns an image with an original size.
*
* @param repoName The repository name.
* @param workspaceName The workspace name.
* @param nodePath The node path.
* @return Response data stream.
* @throws Exception
*
* @anchor ThumbnailRESTService.getOriginImage
*/
@Path("/origin/{repoName}/{workspaceName}/{nodePath:.*}/")
@GET
public Response getOriginImage(@PathParam("repoName") String repoName,
@PathParam("workspaceName") String workspaceName,
@PathParam("nodePath") String nodePath,
@HeaderParam("If-Modified-Since") String ifModifiedSince) throws Exception {
DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
if (!thumbnailService_.isEnableThumbnail())
return Response.ok().header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
Node showingNode = getShowingNode(workspaceName, getNodePath(nodePath));
Node targetNode = getTargetNode(showingNode);
if (targetNode.isNodeType("nt:file") || targetNode.isNodeType("nt:resource")) {
Node content = targetNode;
if (targetNode.isNodeType("nt:file"))
content = targetNode.getNode("jcr:content");
if (ifModifiedSince != null) {
// get last-modified-since from header
Date ifModifiedSinceDate = dateFormat.parse(ifModifiedSince);
// get last modified date of node
Date lastModifiedDate = content.getProperty("jcr:lastModified").getDate().getTime();
// Check if cached resource has not been modifed, return 304 code
if (ifModifiedSinceDate.getTime() >= lastModifiedDate.getTime()) {
return Response.notModified().build();
}
}
String mimeType = content.getProperty("jcr:mimeType").getString();
for (ComponentPlugin plugin : thumbnailService_.getComponentPlugins()) {
if (plugin instanceof ThumbnailPlugin) {
ThumbnailPlugin thumbnailPlugin = (ThumbnailPlugin) plugin;
if (thumbnailPlugin.getMimeTypes().contains(mimeType)) {
InputStream inputStream = content.getProperty("jcr:data").getStream();
return Response.ok(inputStream, "image").header(LAST_MODIFIED_PROPERTY,
dateFormat.format(new Date())).build();
}
}
}
}
return Response.ok().header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
}
/**
* Return an image at an original size from local machine.
*
* @param nodePath The node path.
* @return Response data stream.
* @throws Exception
*
* @anchor ThumbnailRESTService.getOriginImage
*/
@Path("/originImage/{nodePath:.*}/")
@GET
public Response getLocalImage(@PathParam("nodePath") String nodePath,
@HeaderParam("If-Modified-Since") String ifModifiedSince) throws Exception {
DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
if (!thumbnailService_.isEnableThumbnail())
return Response.ok().header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
try {
File file = new File(nodePath);
FileInputStream inputStream = new FileInputStream(file);
BufferedInputStream buf = new BufferedInputStream(inputStream);
return Response.ok(buf, "image").header(LAST_MODIFIED_PROPERTY,
dateFormat.format(new Date())).build();
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error(e);
}
}
return Response.ok().header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
}
private Response getThumbnailByType(String workspaceName, String nodePath,
String propertyName, String ifModifiedSince) throws Exception {
DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
if (!thumbnailService_.isEnableThumbnail())
return Response.ok().header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
Node showingNode = getShowingNode(workspaceName, getNodePath(nodePath));
Node parentNode = showingNode.getParent();
String identifier = ((NodeImpl) showingNode).getInternalIdentifier();
Node targetNode = getTargetNode(showingNode);
if(targetNode.isNodeType("nt:file")) {
Node content = targetNode.getNode("jcr:content");
String mimeType = content.getProperty("jcr:mimeType").getString();
for(ComponentPlugin plugin : thumbnailService_.getComponentPlugins()) {
if(plugin instanceof ThumbnailPlugin) {
ThumbnailPlugin thumbnailPlugin = (ThumbnailPlugin) plugin;
if(thumbnailPlugin.getMimeTypes().contains(mimeType)) {
Node thumbnailFolder = ThumbnailUtils.getThumbnailFolder(parentNode);
Node thumbnailNode = ThumbnailUtils.getThumbnailNode(thumbnailFolder, identifier);
if(!thumbnailNode.hasProperty(propertyName)) {
try {
BufferedImage image = thumbnailPlugin.getBufferedImage(content, targetNode.getPath());
thumbnailService_.addThumbnailImage(thumbnailNode, image, propertyName);
} catch (Exception e) {
throw new Exception("Failed to get image.", e);
}
}
if(ifModifiedSince != null && thumbnailNode.hasProperty(ThumbnailService.THUMBNAIL_LAST_MODIFIED)) {
// get last-modified-since from header
Date ifModifiedSinceDate = dateFormat.parse(ifModifiedSince);
// get last modified date of node
Date lastModifiedDate = thumbnailNode.getProperty(ThumbnailService.THUMBNAIL_LAST_MODIFIED)
.getDate()
.getTime();
// Check if cached resource has not been modifed, return 304 code
if (ifModifiedSinceDate.getTime() >= lastModifiedDate.getTime()) {
return Response.notModified().build();
}
}
InputStream inputStream = null;
if(thumbnailNode.hasProperty(propertyName)) {
inputStream = thumbnailNode.getProperty(propertyName).getStream();
}
return Response.ok(inputStream, "image").header(LAST_MODIFIED_PROPERTY,
dateFormat.format(new Date())).build();
}
}
}
}
return getThumbnailRes(parentNode, identifier, propertyName, ifModifiedSince);
}
private Response getThumbnailRes(Node parentNode,
String identifier,
String propertyName,
String ifModifiedSince) throws Exception {
DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
if(parentNode.hasNode(ThumbnailService.EXO_THUMBNAILS_FOLDER)) {
Node thumbnailFolder = parentNode.getNode(ThumbnailService.EXO_THUMBNAILS_FOLDER);
if(thumbnailFolder.hasNode(identifier)) {
Node thumbnailNode = thumbnailFolder.getNode(identifier);
if (ifModifiedSince != null && thumbnailNode.hasProperty("exo:dateModified")) {
// get last-modified-since from header
Date ifModifiedSinceDate = dateFormat.parse(ifModifiedSince);
// get last modified date of node
Date lastModifiedDate = thumbnailNode.getProperty("exo:dateModified").getDate().getTime();
// Check if cached resource has not been modified, return 304 code
if (ifModifiedSinceDate.getTime() >= lastModifiedDate.getTime()) {
return Response.notModified().build();
}
}
if(thumbnailNode.hasProperty(propertyName)) {
InputStream inputStream = thumbnailNode.getProperty(propertyName).getStream();
return Response.ok(inputStream, "image").header(LAST_MODIFIED_PROPERTY,
dateFormat.format(new Date())).build();
}
}
}
return Response.ok().header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
}
private String getNodePath(String nodePath) throws Exception {
ArrayList<String> encodeNameArr = new ArrayList<String>();
if(!nodePath.equals("/")) {
for(String name : nodePath.split("/")) {
if(name.length() > 0) {
encodeNameArr.add(Text.escapeIllegalJcrChars(name));
}
}
StringBuilder encodedPath = new StringBuilder();
for(String encodedName : encodeNameArr) {
encodedPath.append("/").append(encodedName);
}
nodePath = encodedPath.toString();
}
return nodePath;
}
private Node getTargetNode(Node showingNode) throws Exception {
Node targetNode = null;
if (linkManager_.isLink(showingNode)) {
try {
targetNode = linkManager_.getTarget(showingNode);
} catch (ItemNotFoundException e) {
targetNode = showingNode;
}
} else {
targetNode = showingNode;
}
return targetNode;
}
private Node getShowingNode(String workspaceName, String nodePath) throws Exception {
ManageableRepository repository = repositoryService_.getCurrentRepository();
Session session = WCMCoreUtils.getUserSessionProvider().getSession(workspaceName, repository);
Node showingNode = null;
if(nodePath.equals("/")) showingNode = session.getRootNode();
else {
showingNode = (Node) nodeFinder_.getItem(session, nodePath);
}
return showingNode;
}
}