View Javadoc
1   /*
2    * Copyright (C) 2003-2008 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.wcm.connector.collaboration;
18  
19  import java.io.InputStream;
20  import java.text.DateFormat;
21  import java.text.ParseException;
22  import java.text.SimpleDateFormat;
23  import java.util.Date;
24  
25  import javax.jcr.ItemNotFoundException;
26  import javax.jcr.Node;
27  import javax.jcr.PathNotFoundException;
28  import javax.jcr.version.VersionHistory;
29  import javax.ws.rs.DefaultValue;
30  import javax.ws.rs.GET;
31  import javax.ws.rs.HeaderParam;
32  import javax.ws.rs.Path;
33  import javax.ws.rs.PathParam;
34  import javax.ws.rs.QueryParam;
35  import javax.ws.rs.core.Response;
36  
37  import org.exoplatform.common.http.HTTPStatus;
38  import org.exoplatform.services.jcr.RepositoryService;
39  import org.exoplatform.services.jcr.ext.app.SessionProviderService;
40  import org.exoplatform.services.jcr.ext.common.SessionProvider;
41  import org.exoplatform.services.log.ExoLogger;
42  import org.exoplatform.services.log.Log;
43  import org.exoplatform.services.rest.resource.ResourceContainer;
44  import org.exoplatform.services.wcm.core.NodetypeConstant;
45  import org.exoplatform.services.wcm.core.WCMService;
46  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
47  
48  /**
49   * Gets the image binary data of a given image node.
50   *
51   * @LevelAPI Provisional
52   * 
53   * @anchor RESTImagesRendererService
54   */
55  @Path("/images/")
56  public class RESTImagesRendererService implements ResourceContainer{
57  
58    /** The session provider service. */
59    private SessionProviderService sessionProviderService;
60  
61    /** The repository service. */
62    private RepositoryService repositoryService;
63  
64    /** The log. */
65    private static final Log LOG = ExoLogger.getLogger(RESTImagesRendererService.class.getName());
66  
67    /** The Constant LAST_MODIFIED_PROPERTY. */
68    private static final String LAST_MODIFIED_PROPERTY = "Last-Modified";
69  
70    /** The Constant IF_MODIFIED_SINCE_DATE_FORMAT. */
71    private static final String IF_MODIFIED_SINCE_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";
72  
73    /** Default mime type **/
74    private static String DEFAULT_MIME_TYPE = "image/jpg";
75  
76    /** Mime type property **/
77    private static String PROPERTY_MIME_TYPE = "jcr:mimeType";
78  
79    /**
80     * Instantiates a new REST images renderer service.
81     *
82     * @param repositoryService The repository service.
83     * @param sessionProviderService The session provider service.
84     */
85    public RESTImagesRendererService(RepositoryService repositoryService, SessionProviderService sessionProviderService) {
86      this.repositoryService = repositoryService;
87      this.sessionProviderService = sessionProviderService;
88    }
89  
90    /**
91     * Gets the image binary data of a given image node.
92     * @param repositoryName The repository.
93     * @param workspaceName The workspace.
94     * @param nodeIdentifier The node identifier.
95     * @param param Checks if the document is a file or not. The default value is "file".
96     * @param ifModifiedSince Checks the modification date.
97     * @return The response
98     *
99     * @anchor RESTImagesRendererService.serveImage
100    */
101   @GET
102   @Path("/{repositoryName}/{workspaceName}/{nodeIdentifier}")
103   public Response serveImage(@PathParam("repositoryName") String repositoryName,
104                                      @PathParam("workspaceName") String workspaceName,
105                                      @PathParam("nodeIdentifier") String nodeIdentifier,
106                                      @QueryParam("param") @DefaultValue("file") String param,
107                                      @HeaderParam("If-Modified-Since") String ifModifiedSince) {
108     try {
109       SessionProvider sessionProvider = sessionProviderService.getSessionProvider(null);
110       WCMService wcmService = WCMCoreUtils.getService(WCMService.class);
111       Node node = wcmService.getReferencedContent(sessionProvider, workspaceName, nodeIdentifier);
112       if (node == null) return Response.status(HTTPStatus.NOT_FOUND).build();
113 
114       if ("file".equals(param)) {
115         Node dataNode = null;
116         if(WCMCoreUtils.isNodeTypeOrFrozenType(node, NodetypeConstant.NT_FILE)) {
117           dataNode = node;
118         }else if(node.isNodeType("nt:versionedChild")) {
119           VersionHistory versionHistory = (VersionHistory)node.getProperty("jcr:childVersionHistory").getNode();
120           String versionableUUID = versionHistory.getVersionableUUID();
121           dataNode = sessionProvider.getSession(workspaceName,
122                                                 repositoryService.getCurrentRepository())
123                                     .getNodeByUUID(versionableUUID);
124         }else {
125           return Response.status(HTTPStatus.NOT_FOUND).build();
126         }
127 
128         if (ifModifiedSince != null && isModified(ifModifiedSince, dataNode) == false) {
129           return Response.notModified().build();
130         }
131 
132         DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
133 
134         Node jcrContentNode = dataNode.getNode("jcr:content");
135         String mimeType = DEFAULT_MIME_TYPE;
136         if (jcrContentNode.hasProperty(PROPERTY_MIME_TYPE)) {
137           mimeType = jcrContentNode.getProperty(PROPERTY_MIME_TYPE).getString();
138         }
139         InputStream jcrData = jcrContentNode.getProperty("jcr:data").getStream();
140         return Response.ok(jcrData, mimeType).header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
141       }
142 
143       if (ifModifiedSince != null && isModified(ifModifiedSince, node) == false) {
144         return Response.notModified().build();
145       }
146 
147       DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
148       InputStream jcrData = node.getProperty(param).getStream();
149       return Response.ok(jcrData, DEFAULT_MIME_TYPE).header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date())).build();
150 
151     } catch (PathNotFoundException e) {
152       return Response.status(HTTPStatus.NOT_FOUND).build();
153     }catch (ItemNotFoundException e) {
154       return Response.status(HTTPStatus.NOT_FOUND).build();
155     }catch (Exception e) {
156       if (LOG.isErrorEnabled()) {
157         LOG.error("Error when serveImage: ", e);
158       }
159       return Response.serverError().build();
160     }
161   }
162 
163   /**
164    * Gets the last modified date of a node.
165   + * @param node A specific node.
166   + * @return The last modified date.
167   + * @throws Exception
168   + */
169   private Date getLastModifiedDate(Node node) throws Exception {
170      Date lastModifiedDate = null;
171      if (node.hasNode("jcr:content") && node.getNode("jcr:content").hasProperty("jcr:lastModified")) {
172        lastModifiedDate = node.getNode("jcr:content").getProperty("jcr:lastModified").getDate().getTime();
173      } else if (node.hasProperty("exo:dateModified")) {
174          lastModifiedDate = node.getProperty("exo:dateModified").getDate().getTime();
175      } else if (node.hasProperty("jcr:created")){
176        lastModifiedDate = node.getProperty("jcr:created").getDate().getTime();
177      }
178      return lastModifiedDate;
179   }
180 
181   /**
182    * Checks if resources were modified or not.
183    * @param ifModifiedSince The date when the node is modified.
184    * @param node A specific node.
185    * @return
186    * @throws Exception
187    */
188   private boolean isModified(String ifModifiedSince, Node node) throws Exception {
189      // get last-modified-since from header
190      DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
191      if(ifModifiedSince == null || ifModifiedSince.length() == 0)
192        return false;
193      try {
194        Date ifModifiedSinceDate = dateFormat.parse(ifModifiedSince);
195        // get last modified date of node
196        Date lastModifiedDate = getLastModifiedDate(node);
197        // Check if cached resource has not been modifed, return 304 code
198        if (lastModifiedDate != null && ifModifiedSinceDate != null &&
199            ifModifiedSinceDate.getTime() >= lastModifiedDate.getTime()) {
200          return false;
201        }
202        return true;
203      } catch(ParseException pe) {
204        return false;
205      }
206 
207   }
208 
209 }