View Javadoc
1   /**
2    * This file Copyright (c) 2015-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.dam.imaging;
35  
36  import static com.google.common.net.MediaType.*;
37  
38  import info.magnolia.context.MgnlContext;
39  import info.magnolia.dam.api.Asset;
40  import info.magnolia.dam.api.AssetDecorator;
41  import info.magnolia.dam.api.AssetRenderer;
42  import info.magnolia.dam.api.AssetRendition;
43  import info.magnolia.dam.api.DamException;
44  import info.magnolia.dam.jcr.AssetNodeTypes;
45  import info.magnolia.dam.jcr.DamConstants;
46  import info.magnolia.dam.jcr.JcrAsset;
47  import info.magnolia.imaging.ImagingSupport;
48  import info.magnolia.module.site.Site;
49  import info.magnolia.module.site.functions.SiteFunctions;
50  
51  import java.io.InputStream;
52  import java.util.Arrays;
53  import java.util.List;
54  
55  import javax.inject.Inject;
56  import javax.inject.Provider;
57  import javax.jcr.Node;
58  import javax.jcr.Property;
59  import javax.jcr.RepositoryException;
60  
61  import org.apache.jackrabbit.JcrConstants;
62  
63  import com.google.common.net.MediaType;
64  
65  /**
66   * Asset renderer using the {@link info.magnolia.imaging.ImagingSupport} to generate renditions.
67   *
68   * @see info.magnolia.imaging.ImagingSupport
69   */
70  public class ImagingBasedAssetRenderer implements AssetRenderer {
71  
72      private final ImagingSupport imagingSupport;
73      private final Provider<Site> siteProvider;
74      private final SiteFunctions siteFunctions;
75  
76      /**
77       * Input {@link com.google.common.net.MediaType}s we currently support in imaging.
78       */
79      private final List<MediaType> mediaTypes = Arrays.asList(BMP, JPEG, GIF, TIFF, PNG);
80  
81      /**
82       * Output {@link com.google.common.net.MediaType}s we currently support in imaging.
83       */
84      private final List<MediaType> outputMediaTypes = Arrays.asList(JPEG, GIF, PNG);
85  
86      @Inject
87      public ImagingBasedAssetRenderer(Provider<Site> siteProvider, SiteFunctions siteFunctions, ImagingSupport imagingSupport) {
88          this.siteProvider = siteProvider;
89          this.siteFunctions = siteFunctions;
90          this.imagingSupport = imagingSupport;
91      }
92  
93      /**
94       * Checks if the provided {@link com.google.common.net.MediaType}s are supported by this
95       * {@link info.magnolia.dam.api.AssetRenderer}.
96       */
97      @Override
98      public boolean supports(MediaType from, MediaType to) {
99          if (from == null || to == null) {
100             return false;
101         }
102         return mediaTypes.contains(from) && outputMediaTypes.contains(to);
103     }
104 
105     /**
106      * Checks if the provided {@link info.magnolia.dam.api.Asset} and the {@link com.google.common.net.MediaType} can
107      * be rendered by this {@link info.magnolia.dam.api.AssetRenderer}.
108      *
109      * @see #supports(com.google.common.net.MediaType, com.google.common.net.MediaType)
110      */
111     @Override
112     public boolean canRender(Asset asset, MediaType to) {
113         final Asset unwrappedAsset = unwrapDecoratedAsset(asset);
114         if (unwrappedAsset == null || !(unwrappedAsset instanceof JcrAsset)) {
115             return false;
116         }
117         return supports(MediaType.parse(unwrappedAsset.getMimeType()), to);
118     }
119 
120     /**
121      * Renders an {@link info.magnolia.dam.api.AssetRendition} fo the provided {@link info.magnolia.dam.api.Asset}.
122      */
123     @Override
124     public AssetRendition render(Asset asset, MediaType to, String renditionName) {
125         final Site site = siteProvider.get();
126 
127         final ImagingSupport imagingSupport;
128         if (site == null) {
129             imagingSupport = this.imagingSupport;
130         } else {
131             imagingSupport = siteFunctions.theme(site).getImaging();
132         }
133 
134         asset = unwrapDecoratedAsset(asset); // Unwrap legacy assets
135 
136         if (asset instanceof JcrAsset) {
137             try {
138                 final Property jcrDataProperty = getBinaryProperty((JcrAsset) asset);
139                 final String link = imagingSupport.createLink(jcrDataProperty, renditionName);
140 
141                 return new ImagingBasedAssetRendition(asset, link, to, renditionName);
142             } catch (RepositoryException e) {
143                 throw new AssetRenditionException(String.format("Asset rendition '%s' could not be generated.", renditionName), e);
144             }
145         } else {
146             throw new AssetRenditionException("Asset renditions of non-jcr assets cannot be generated.", new IllegalArgumentException());
147         }
148     }
149 
150     /**
151      * Will return the binary {@link javax.jcr.Property} of a {@link info.magnolia.dam.jcr.JcrAsset}.
152      */
153     private Property getBinaryProperty(JcrAsset jcrAsset) throws RepositoryException {
154         final String assetId = jcrAsset.getItemKey().getAssetId();
155         final Node assetNode = MgnlContext.getJCRSession(DamConstants.WORKSPACE).getNodeByIdentifier(assetId);
156         final Node resourceNode = AssetNodeTypes.AssetResource.getResourceNodeFromAsset(assetNode);
157         return resourceNode.getProperty(JcrConstants.JCR_DATA);
158     }
159 
160     /**
161      * Unwraps an {@link info.magnolia.dam.api.Asset} if necessary.
162      *
163      * <p>This is required for being backwards compatible to legacy code that might still wrap
164      * {@link info.magnolia.dam.api.Asset}s.</p>
165      */
166     private Asset unwrapDecoratedAsset(Asset asset) {
167         if (asset instanceof AssetDecorator) {
168             return AssetDecorator.unwrap(asset);
169         }
170         return asset;
171     }
172 
173     /**
174      * {@link info.magnolia.dam.api.AssetRendition} based on the imaging module.
175      */
176     private static class ImagingBasedAssetRendition implements AssetRendition {
177 
178         private final Asset asset;
179         private final String link;
180         private final String renditionName;
181         private final MediaType mimeType;
182 
183         public ImagingBasedAssetRendition(Asset asset, String link, MediaType mediaType, String renditionName) {
184             this.asset = asset;
185             this.link = link;
186             this.renditionName = renditionName;
187             this.mimeType = mediaType;
188         }
189 
190         @Override
191         public String getRenditionName() {
192             return renditionName;
193         }
194 
195         @Override
196         public String getMimeType() {
197             return mimeType.toString();
198         }
199 
200         /**
201          * Currently we do not support returning an {@link java.io.InputStream} of the rendition itself.
202          * We should not simply return {@link info.magnolia.dam.api.Asset#getContentStream()}!
203          */
204         @Override
205         public InputStream getStream() {
206             throw new UnsupportedOperationException();
207         }
208 
209         @Override
210         public Asset getAsset() {
211             return asset;
212         }
213 
214         @Override
215         public String getLink() {
216             return link;
217         }
218 
219     }
220 
221     /**
222      * {@link info.magnolia.dam.api.DamException} thrown when an error occurs while rendering an
223      * {@link info.magnolia.dam.api.AssetRendition}.
224      */
225     protected static class AssetRenditionException extends DamException {
226         public AssetRenditionException(String message, Throwable throwable) {
227             super(message, throwable);
228         }
229     }
230 
231 }