001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 */ 017package org.nuxeo.ecm.platform.preview.adapter.base; 018 019import java.io.File; 020import java.io.IOException; 021import java.io.InputStream; 022import java.util.List; 023 024import org.apache.commons.codec.digest.DigestUtils; 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027 028import org.nuxeo.ecm.core.api.Blob; 029import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 030import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder; 031import org.nuxeo.ecm.core.convert.api.ConversionException; 032import org.nuxeo.ecm.core.convert.api.ConversionService; 033import org.nuxeo.ecm.platform.mimetype.MimetypeDetectionException; 034import org.nuxeo.ecm.platform.mimetype.MimetypeNotFoundException; 035import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; 036import org.nuxeo.ecm.platform.preview.adapter.MimeTypePreviewer; 037import org.nuxeo.ecm.platform.preview.adapter.PreviewAdapterManager; 038import org.nuxeo.ecm.platform.preview.api.NothingToPreviewException; 039import org.nuxeo.ecm.platform.preview.api.PreviewException; 040import org.nuxeo.runtime.api.Framework; 041 042/** 043 * Base class for preview based on "on the fly" HTML transformers 044 * 045 * @author tiry 046 */ 047public class ConverterBasedHtmlPreviewAdapter extends AbstractHtmlPreviewAdapter { 048 049 private static final Log log = LogFactory.getLog(ConverterBasedHtmlPreviewAdapter.class); 050 051 protected String defaultFieldXPath; 052 053 protected MimetypeRegistry mimeTypeService; 054 055 public ConversionService getConversionService() { 056 return Framework.getService(ConversionService.class); 057 } 058 059 @Override 060 protected PreviewAdapterManager getPreviewManager() { 061 return Framework.getService(PreviewAdapterManager.class); 062 } 063 064 protected static String getMimeType(Blob blob) { 065 if (blob == null) { 066 return null; 067 } 068 069 String srcMT = blob.getMimeType(); 070 if (srcMT == null || srcMT.startsWith("application/octet-stream")) { 071 // call MT Service 072 try { 073 MimetypeRegistry mtr = Framework.getService(MimetypeRegistry.class); 074 srcMT = mtr.getMimetypeFromFilenameAndBlobWithDefault(blob.getFilename(), blob, 075 "application/octet-stream"); 076 log.debug("mime type service returned " + srcMT); 077 } catch (MimetypeDetectionException e) { 078 log.warn("error while calling Mimetype service", e); 079 } 080 } 081 return srcMT; 082 } 083 084 protected String getMimeType(String xpath) { 085 BlobHolder blobHolder2preview = getBlobHolder2preview(xpath); 086 Blob blob = getBlob2preview(blobHolder2preview); 087 return getMimeType(blob); 088 } 089 090 protected String getDefaultPreviewFieldXPath() { 091 return defaultFieldXPath; 092 } 093 094 public void setDefaultPreviewFieldXPath(String xPath) { 095 defaultFieldXPath = xPath; 096 } 097 098 @Override 099 public List<Blob> getPreviewBlobs() throws PreviewException { 100 return getPreviewBlobs(getDefaultPreviewFieldXPath()); 101 } 102 103 @Override 104 public boolean hasPreview(String xpath) { 105 String srcMT = getMimeType(xpath); 106 MimeTypePreviewer mtPreviewer = getPreviewManager().getPreviewer(srcMT); 107 return mtPreviewer != null || getConversionService().getConverterName(srcMT, "text/html") != null; 108 } 109 110 @Override 111 public List<Blob> getPreviewBlobs(String xpath) throws PreviewException { 112 BlobHolder blobHolder2preview = getBlobHolder2preview(xpath); 113 Blob blob2Preview = getBlob2preview(blobHolder2preview); 114 115 String srcMT = getMimeType(xpath); 116 log.debug("Source type for HTML preview =" + srcMT); 117 MimeTypePreviewer mtPreviewer = getPreviewManager().getPreviewer(srcMT); 118 if (mtPreviewer != null) { 119 return mtPreviewer.getPreview(blob2Preview, adaptedDoc); 120 } 121 122 String converterName = getConversionService().getConverterName(srcMT, "text/html"); 123 if (converterName == null) { 124 log.debug("No dedicated converter found, using generic"); 125 converterName = "any2html"; 126 } 127 128 BlobHolder result; 129 try { 130 result = getConversionService().convert(converterName, blobHolder2preview, null); 131 setMimeType(result); 132 setDigest(result); 133 return result.getBlobs(); 134 } catch (ConversionException e) { 135 throw new PreviewException(e.getMessage(), e); 136 } 137 } 138 139 /** 140 * @since 5.7.3 141 */ 142 private Blob getBlob2preview(BlobHolder blobHolder2preview) throws PreviewException { 143 Blob blob2Preview = blobHolder2preview.getBlob(); 144 if (blob2Preview == null) { 145 throw new NothingToPreviewException("Can not preview a document without blob"); 146 } else { 147 return blob2Preview; 148 } 149 } 150 151 /** 152 * Returns a blob holder suitable for a preview. 153 * 154 * @since 5.7.3 155 */ 156 private BlobHolder getBlobHolder2preview(String xpath) { 157 if ((xpath == null) || ("default".equals(xpath))) { 158 return adaptedDoc.getAdapter(BlobHolder.class); 159 } else { 160 return new DocumentBlobHolder(adaptedDoc, xpath); 161 } 162 } 163 164 protected void setMimeType(BlobHolder result) { 165 for (Blob blob : result.getBlobs()) { 166 if ((blob.getMimeType() == null || blob.getMimeType().startsWith("application/octet-stream")) 167 && blob.getFilename().endsWith("html")) { 168 String mimeTpye = getMimeType(blob); 169 blob.setMimeType(mimeTpye); 170 } 171 } 172 } 173 174 protected void setDigest(BlobHolder result) { 175 for (Blob blob : result.getBlobs()) { 176 if (blob.getDigest() == null) { 177 try (InputStream stream = blob.getStream()) { 178 String digest = DigestUtils.md5Hex(stream); 179 blob.setDigest(digest); 180 } catch (IOException e) { 181 log.warn("Unable to compute digest of blob.", e); 182 } 183 } 184 } 185 } 186 187 public String getMimeType(File file) throws ConversionException { 188 try { 189 return getMimeTypeService().getMimetypeFromFile(file); 190 } catch (ConversionException e) { 191 throw new ConversionException("Could not get MimeTypeRegistry"); 192 } catch (MimetypeNotFoundException e) { 193 return "application/octet-stream"; 194 } catch (MimetypeDetectionException e) { 195 return "application/octet-stream"; 196 } 197 } 198 199 public MimetypeRegistry getMimeTypeService() throws ConversionException { 200 if (mimeTypeService == null) { 201 mimeTypeService = Framework.getService(MimetypeRegistry.class); 202 } 203 return mimeTypeService; 204 } 205 206 @Override 207 public void cleanup() { 208 209 } 210 211 @Override 212 public boolean cachable() { 213 return true; 214 } 215 216 @Override 217 public boolean hasBlobToPreview() throws PreviewException { 218 String xpath = getDefaultPreviewFieldXPath(); 219 Blob blob2Preview; 220 try { 221 blob2Preview = getBlob2preview(getBlobHolder2preview(xpath)); 222 } catch (NothingToPreviewException e) { 223 return false; 224 } 225 return blob2Preview != null; 226 } 227 228}