View Javadoc
1   /*
2    * Copyright (C) 2003-2007 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.services.cms.thumbnail.impl;
18  
19  import java.awt.image.BufferedImage;
20  import java.io.InputStream;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.GregorianCalendar;
24  import java.util.List;
25  
26  import javax.imageio.ImageIO;
27  import javax.jcr.ItemExistsException;
28  import javax.jcr.Node;
29  import javax.jcr.NodeIterator;
30  import javax.jcr.PathNotFoundException;
31  import javax.jcr.RepositoryException;
32  import javax.jcr.Session;
33  
34  import org.exoplatform.container.component.ComponentPlugin;
35  import org.exoplatform.container.xml.InitParams;
36  import org.exoplatform.services.cms.impl.ImageUtils;
37  import org.exoplatform.services.cms.thumbnail.ThumbnailPlugin;
38  import org.exoplatform.services.cms.thumbnail.ThumbnailService;
39  import org.exoplatform.services.jcr.RepositoryService;
40  import org.exoplatform.services.jcr.impl.core.NodeImpl;
41  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
42  
43  /**
44   * Created by The eXo Platform SARL
45   * Author : Dang Van Minh
46   *          minh.dang@exoplatform.com
47   * Oct 10, 2008 1:58:10 PM
48   */
49  public class ThumbnailServiceImpl implements ThumbnailService {
50  
51    final private static String JCR_CONTENT = "jcr:content";
52    final private static String JCR_MIMETYPE = "jcr:mimeType";
53    final private static String JCR_DATA = "jcr:data";
54    final private static String NT_FILE = "nt:file";
55  
56    private boolean isEnableThumbnail_ = false;
57    private String smallSize_;
58    private String mediumSize_;
59    private String bigSize_;
60    private String mimeTypes_;
61    private List<ComponentPlugin> plugins_ = new ArrayList<ComponentPlugin>();
62  
63    public ThumbnailServiceImpl(InitParams initParams) throws Exception {
64      smallSize_ = initParams.getValueParam("smallSize").getValue();
65      mediumSize_ = initParams.getValueParam("mediumSize").getValue();
66      bigSize_ = initParams.getValueParam("bigSize").getValue();
67      mimeTypes_ = initParams.getValueParam("mimetypes").getValue();
68      isEnableThumbnail_ = Boolean.parseBoolean(initParams.getValueParam("enable").getValue());
69    }
70  
71    /**
72     * {@inheritDoc}
73     */
74    public List<Node> getFlowImages(Node node) throws Exception {
75      NodeIterator nodeIter = node.getNodes();
76      List<Node> listNodes = new ArrayList<Node>();
77      Node thumbnailNode = null;
78      while(nodeIter.hasNext()) {
79        Node childNode = nodeIter.nextNode();
80        thumbnailNode = addThumbnailNode(childNode);
81        if(thumbnailNode != null && thumbnailNode.isNodeType(EXO_THUMBNAIL) && thumbnailNode.hasProperty(BIG_SIZE)) {
82          listNodes.add(childNode);
83        }
84      }
85      return listNodes;
86    }
87  
88    /**
89     * {@inheritDoc}
90     */
91    public List<Node> getAllFileInNode(Node node) throws RepositoryException {
92      List<Node> fileListNodes = new ArrayList<Node>();
93      NodeIterator nodeIter = node.getNodes();
94      Node childNode = null;
95      while(nodeIter.hasNext()) {
96        childNode = nodeIter.nextNode();
97        if(childNode.isNodeType(NT_FILE)) {
98          fileListNodes.add(childNode);
99        }
100     }
101     return fileListNodes;
102   }
103 
104   /**
105    * {@inheritDoc}
106    */
107   public List<Node> getFileNodesByType(Node node, String jcrMimeType) throws RepositoryException {
108     List<Node> fileListNodes = getAllFileInNode(node);
109     List<Node> listNodes = new ArrayList<Node>();
110     Node contentNode = null;
111     for(Node childNode : fileListNodes) {
112       contentNode = childNode.getNode(JCR_CONTENT);
113       if(contentNode.getProperty(JCR_MIMETYPE).getString().equals(jcrMimeType)) {
114         listNodes.add(childNode);
115       }
116     }
117     return listNodes;
118   }
119 
120   /**
121    * {@inheritDoc}
122    */
123   public boolean isEnableThumbnail() {
124     return isEnableThumbnail_;
125   }
126 
127   /**
128    * {@inheritDoc}
129    */
130   public void setEnableThumbnail(boolean isEnable) {
131     isEnableThumbnail_ = isEnable;
132   }
133 
134   /**
135    * {@inheritDoc}
136    */
137   public InputStream getThumbnailImage(Node node, String thumbnailType) throws Exception {
138     Node thumbnailNode = addThumbnailNode(node);
139     if(thumbnailNode != null && thumbnailNode.hasProperty(thumbnailType)) {
140       return thumbnailNode.getProperty(thumbnailType).getStream();
141     }
142     return null;
143   }
144 
145   /**
146    * {@inheritDoc}
147    */
148   public void addThumbnailImage(Node thumbnailNode, BufferedImage image, String propertyName) throws Exception {
149     if(propertyName.equals(SMALL_SIZE)) parseImageSize(thumbnailNode, image, smallSize_, SMALL_SIZE);
150     else if(propertyName.equals(MEDIUM_SIZE)) parseImageSize(thumbnailNode, image, mediumSize_, MEDIUM_SIZE);
151     else if(propertyName.equals(BIG_SIZE)) parseImageSize(thumbnailNode, image, bigSize_, BIG_SIZE);
152     else parseImageSize(thumbnailNode, image, propertyName.substring(4), propertyName, false);
153   }
154 
155   /**
156    * {@inheritDoc}
157    */
158   public void createSpecifiedThumbnail(Node node, BufferedImage image, String propertyName) throws Exception {
159     addThumbnailImage(addThumbnailNode(node), image, propertyName);
160   }
161 
162   /**
163    * {@inheritDoc}
164    */
165   public void createThumbnailImage(Node node, BufferedImage image, String mimeType) throws Exception {
166     Node thumbnailNode = addThumbnailNode(node);
167     if(thumbnailNode != null) {
168       if(mimeType.startsWith("image")) processImage2Image(thumbnailNode, image);
169       thumbnailNode.setProperty(THUMBNAIL_LAST_MODIFIED, new GregorianCalendar());
170       thumbnailNode.getSession().save();
171     }
172   }
173 
174   /**
175    * {@inheritDoc}
176    */
177   public void processThumbnailList(List<Node> listNodes, String type) throws Exception {
178     for(Node node : listNodes) {
179       Node thumbnailNode = addThumbnailNode(node);
180       if(thumbnailNode != null && !thumbnailNode.hasProperty(THUMBNAIL_LAST_MODIFIED) &&
181           node.isNodeType(NT_FILE)) {
182         Node contentNode = node.getNode(JCR_CONTENT);
183         if(contentNode.getProperty(JCR_MIMETYPE).getString().startsWith("image")) {
184           BufferedImage image = ImageIO.read(contentNode.getProperty(JCR_DATA).getStream());
185           addThumbnailImage(thumbnailNode, image, type);
186         }
187       }
188     }
189   }
190 
191   /**
192    * {@inheritDoc}
193    */
194   public List<String> getMimeTypes() {
195     return Arrays.asList(mimeTypes_.split(";"));
196   }
197 
198   /**
199    * {@inheritDoc}
200    */
201   public Node addThumbnailNode(Node node) throws Exception {
202     Node parentNode = node.getParent();
203     Node thumbnailFolder = ThumbnailUtils.getThumbnailFolder(parentNode);
204     String identifier = ((NodeImpl) node).getInternalIdentifier();
205     Node thumbnailNode = ThumbnailUtils.getThumbnailNode(thumbnailFolder, identifier);
206     return thumbnailNode;
207   }
208 
209   /**
210    * {@inheritDoc}
211    */
212   public Node getThumbnailNode(Node node) throws Exception {
213     try {
214       String nodePath = node.getPath();
215       String thumPath = nodePath.replace(nodePath.substring(nodePath.lastIndexOf("/") + 1), EXO_THUMBNAILS_FOLDER);
216       Node thumbnailFolder = (Node)node.getSession().getItem(thumPath);
217       return thumbnailFolder.getNode(((NodeImpl) node).getInternalIdentifier());
218     } catch(Exception e) {
219       return null;
220     }
221   }
222 
223   /**
224    * {@inheritDoc}
225    */
226   public void processRemoveThumbnail(Node showingNode) throws Exception {
227     Node parentNode = showingNode.getParent();
228     if(parentNode.hasNode(EXO_THUMBNAILS_FOLDER)) {
229       Node thumbnailFolder = parentNode.getNode(EXO_THUMBNAILS_FOLDER);
230       try {
231         String workspace = parentNode.getSession().getWorkspace().getName();
232         RepositoryService repositoryService = WCMCoreUtils.getService(RepositoryService.class);
233         Session systemSession = WCMCoreUtils.getSystemSessionProvider().getSession(workspace, repositoryService.getCurrentRepository());
234         thumbnailFolder = (Node) systemSession.getItem(thumbnailFolder.getPath());
235         thumbnailFolder.getNode(((NodeImpl) showingNode).getInternalIdentifier()).remove();
236         thumbnailFolder.getSession().save();
237       } catch(PathNotFoundException path) {
238         return;
239       }
240     }
241   }
242 
243   /**
244    * {@inheritDoc}
245    */
246   public void copyThumbnailNode(Node srcThumbnailNode, Node destNode) throws Exception {
247 
248     // Validate arguments
249     if (srcThumbnailNode == null || destNode == null) {
250       return;
251     }
252 
253     // Copy thumbnail to destination node
254     Node destThumbnailNode = this.addThumbnailNode(destNode);
255     if (srcThumbnailNode.hasProperty(SMALL_SIZE)) {
256       destThumbnailNode.setProperty(SMALL_SIZE, srcThumbnailNode.getProperty(SMALL_SIZE).getValue());
257     }
258 
259     if (srcThumbnailNode.hasProperty(MEDIUM_SIZE)) {
260       destThumbnailNode.setProperty(MEDIUM_SIZE, srcThumbnailNode.getProperty(MEDIUM_SIZE).getValue());
261     }
262 
263     if (srcThumbnailNode.hasProperty(BIG_SIZE)) {
264       destThumbnailNode.setProperty(BIG_SIZE, srcThumbnailNode.getProperty(BIG_SIZE).getValue());
265     }
266 
267     destThumbnailNode.save();
268   }
269 
270   public void addPlugin(ComponentPlugin plugin) {
271     if(plugin instanceof ThumbnailPlugin) plugins_.add(plugin);
272   }
273 
274   public List<ComponentPlugin> getComponentPlugins() {
275     return plugins_;
276   }
277 
278   /**
279    * Put data from image to 3 property : exo:smallSizes, exo:mediumSizes, exo:bigSizes
280    * with each property, image is parsed to correlative size
281    * @param node
282    * @param image
283    * @throws Exception
284    */
285   private void processImage2Image(Node node, BufferedImage image) throws Exception {
286     parseImageSize(node, image, smallSize_, SMALL_SIZE);
287     parseImageSize(node, image, mediumSize_, MEDIUM_SIZE);
288     parseImageSize(node, image, bigSize_, BIG_SIZE);
289   }
290 
291   /**
292    * Put image data to property name of node with given height and width
293    * @param thumbnailNode
294    * @param image
295    * @param width
296    * @param height
297    * @param propertyName
298    * @throws Exception
299    */
300   private void createThumbnailImage(Node thumbnailNode, BufferedImage image, int width, int height,
301       String propertyName, boolean crop) throws Exception {
302     if (width>1600) width=1600;
303     if (height>1600) height=1600;
304     InputStream thumbnailStream = ImageUtils.scaleImage(image, width, height, crop);
305     try {
306       thumbnailNode.setProperty(propertyName, thumbnailStream);
307       thumbnailNode.getSession().save();
308       thumbnailNode.setProperty(THUMBNAIL_LAST_MODIFIED, new GregorianCalendar());
309       thumbnailNode.getSession().save();
310     } catch (ItemExistsException e) {
311       return;
312     } finally {
313       thumbnailStream.close();
314     }
315   }
316 
317   /**
318    * Analysis size which has format (width x height) and call method createThumbnailImage
319    * to put data into propertyName of node
320    * @param node
321    * @param image
322    * @param size
323    * @param propertyName
324    * @throws Exception
325    */
326   private void parseImageSize(Node node, BufferedImage image, String size, String propertyName) throws Exception {
327     parseImageSize(node, image, size, propertyName, false);
328   }
329   /**
330    * Analysis size which has format (width x height) and call method createThumbnailImage
331    * to put data into propertyName of node
332    * @param node
333    * @param image
334    * @param size
335    * @param propertyName
336    * @throws Exception
337    */
338   private void parseImageSize(Node node, BufferedImage image, String size, String propertyName, boolean crop) throws Exception {
339     int width = 0;
340     int height = 0;
341     if (size.startsWith("x")) {
342       height = Integer.parseInt(size.substring(1));
343     } else if (size.endsWith("x")) {
344       width = Integer.parseInt(size.substring(0, size.length()-1));
345     } else if(size.indexOf("x") > -1) {
346       String[] imageSize = size.split("x");
347       width = Integer.parseInt(imageSize[0]);
348       height = Integer.parseInt(imageSize[1]);
349     }
350     createThumbnailImage(node, image, width, height, propertyName, crop);
351   }
352 }