/* * Copyright (C) 2003-2008 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.io.InputStream; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import javax.jcr.ItemNotFoundException; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.version.VersionHistory; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import org.exoplatform.common.http.HTTPStatus; import org.exoplatform.services.jcr.RepositoryService; import org.exoplatform.services.jcr.ext.app.SessionProviderService; import org.exoplatform.services.jcr.ext.common.SessionProvider; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.rest.resource.ResourceContainer; import org.exoplatform.services.wcm.core.NodetypeConstant; import org.exoplatform.services.wcm.core.WCMService; import org.exoplatform.services.wcm.utils.WCMCoreUtils; /** * Gets the image binary data of a given image node. * * @LevelAPI Provisional * * @anchor RESTImagesRendererService */ @Path("/images/") public class RESTImagesRendererService implements ResourceContainer{ /** The session provider service. */ private SessionProviderService sessionProviderService; /** The repository service. */ private RepositoryService repositoryService; /** The log. */ private static final Log LOG = ExoLogger.getLogger(RESTImagesRendererService.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"; /** Default mime type **/ private static String DEFAULT_MIME_TYPE = "image/jpg"; /** Mime type property **/ private static String PROPERTY_MIME_TYPE = "jcr:mimeType"; /** * Instantiates a new REST images renderer service. * * @param repositoryService The repository service. * @param sessionProviderService The session provider service. */ public RESTImagesRendererService(RepositoryService repositoryService, SessionProviderService sessionProviderService) { this.repositoryService = repositoryService; this.sessionProviderService = sessionProviderService; } /** * Gets the image binary data of a given image node. * @param repositoryName The repository. * @param workspaceName The workspace. * @param nodeIdentifier The node identifier. * @param param Checks if the document is a file or not. The default value is "file". * @param ifModifiedSince Checks the modification date. * @return The response * * @anchor RESTImagesRendererService.serveImage */ @GET @Path("/{repositoryName}/{workspaceName}/{nodeIdentifier}") public Response serveImage(@PathParam("repositoryName") String repositoryName, @PathParam("workspaceName") String workspaceName, @PathParam("nodeIdentifier") String nodeIdentifier, @QueryParam("param") @DefaultValue("file") String param, @HeaderParam("If-Modified-Since") String ifModifiedSince) { try { SessionProvider sessionProvider = sessionProviderService.getSessionProvider(null); WCMService wcmService = WCMCoreUtils.getService(WCMService.class); Node node = wcmService.getReferencedContent(sessionProvider, workspaceName, nodeIdentifier); if (node == null) return Response.status(HTTPStatus.NOT_FOUND).build(); if ("file".equals(param)) { Node dataNode = null; if(WCMCoreUtils.isNodeTypeOrFrozenType(node, NodetypeConstant.NT_FILE)) { dataNode = node; }else if(node.isNodeType("nt:versionedChild")) { VersionHistory versionHistory = (VersionHistory)node.getProperty("jcr:childVersionHistory").getNode(); String versionableUUID = versionHistory.getVersionableUUID(); dataNode = sessionProvider.getSession(workspaceName, repositoryService.getCurrentRepository()) .getNodeByUUID(versionableUUID); }else { return Response.status(HTTPStatus.NOT_FOUND).build(); } if (ifModifiedSince != null && isModified(ifModifiedSince, dataNode) == false) { return Response.notModified().build(); } DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT); Node jcrContentNode = dataNode.getNode("jcr:content"); String mimeType = DEFAULT_MIME_TYPE; if (jcrContentNode.hasProperty(PROPERTY_MIME_TYPE)) { mimeType = jcrContentNode.getProperty(PROPERTY_MIME_TYPE).getString(); } InputStream jcrData = jcrContentNode.getProperty("jcr:data").getStream(); return Response.ok(jcrData, mimeType).header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build(); } if (ifModifiedSince != null && isModified(ifModifiedSince, node) == false) { return Response.notModified().build(); } DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT); InputStream jcrData = node.getProperty(param).getStream(); return Response.ok(jcrData, DEFAULT_MIME_TYPE).header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build(); } catch (PathNotFoundException e) { return Response.status(HTTPStatus.NOT_FOUND).build(); }catch (ItemNotFoundException e) { return Response.status(HTTPStatus.NOT_FOUND).build(); }catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Error when serveImage: ", e); } return Response.serverError().build(); } } /** * Gets the last modified date of a node. + * @param node A specific node. + * @return The last modified date. + * @throws Exception + */ private Date getLastModifiedDate(Node node) throws Exception { Date lastModifiedDate = null; if (node.hasNode("jcr:content") && node.getNode("jcr:content").hasProperty("jcr:lastModified")) { lastModifiedDate = node.getNode("jcr:content").getProperty("jcr:lastModified").getDate().getTime(); } else if (node.hasProperty("exo:dateModified")) { lastModifiedDate = node.getProperty("exo:dateModified").getDate().getTime(); } else if (node.hasProperty("jcr:created")){ lastModifiedDate = node.getProperty("jcr:created").getDate().getTime(); } return lastModifiedDate; } /** * Checks if resources were modified or not. * @param ifModifiedSince The date when the node is modified. * @param node A specific node. * @return * @throws Exception */ private boolean isModified(String ifModifiedSince, Node node) throws Exception { // get last-modified-since from header DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT); if(ifModifiedSince == null || ifModifiedSince.length() == 0) return false; try { Date ifModifiedSinceDate = dateFormat.parse(ifModifiedSince); // get last modified date of node Date lastModifiedDate = getLastModifiedDate(node); // Check if cached resource has not been modifed, return 304 code if (lastModifiedDate != null && ifModifiedSinceDate != null && ifModifiedSinceDate.getTime() >= lastModifiedDate.getTime()) { return false; } return true; } catch(ParseException pe) { return false; } } }