PDFViewerService.java
/*
* Copyright (C) 2003-2012 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.exoplatform.services.pdfviewer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jcr.Node;
import org.apache.commons.lang.StringUtils;
import org.artofsolving.jodconverter.office.OfficeException;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.ValueParam;
import org.exoplatform.services.cache.CacheService;
import org.exoplatform.services.cache.ExoCache;
import org.exoplatform.services.cms.impl.Utils;
import org.exoplatform.services.cms.jodconverter.JodConverterService;
import org.exoplatform.services.cms.mimetype.DMSMimeTypeResolver;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.icepdf.core.pobjects.Document;
/**
* Created by The eXo Platform SAS Author : Nguyen The Vinh From ECM Of
* eXoPlatform vinh_nguyen@exoplatform.com 6 Jul 2012
*/
public class PDFViewerService {
private static final Log LOG = ExoLogger.getLogger(PDFViewerService.class.getName());
private static final int MAX_NAME_LENGTH = 150;
public static final long DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
public static final long DEFAULT_MAX_PAGES = 99;
public static final String MAX_FILE_SIZE_PARAM_NAME = "maxFileSize";
public static final String MAX_PAGES_PARAM_NAME = "maxPages";
private static final String CACHE_NAME = "ecms.PDFViewerService";
private JodConverterService jodConverter_;
private ExoCache<Serializable, Object> pdfCache;
private long maxFileSize;
private long maxPages;
public PDFViewerService(RepositoryService repositoryService,
CacheService caService,
JodConverterService jodConverter,
InitParams initParams) throws Exception {
jodConverter_ = jodConverter;
pdfCache = caService.getCacheInstance(CACHE_NAME);
maxFileSize = DEFAULT_MAX_FILE_SIZE;
maxPages = DEFAULT_MAX_PAGES;
if (initParams != null) {
ValueParam maxFileSizeValueParam = initParams.getValueParam(MAX_FILE_SIZE_PARAM_NAME);
if (maxFileSizeValueParam != null) {
try {
maxFileSize = Long.parseLong(maxFileSizeValueParam.getValue()) * 1024 * 1024;
} catch (NumberFormatException e) {
LOG.warn("Parameter " + MAX_FILE_SIZE_PARAM_NAME + " for document preview is not a valid number ("
+ maxFileSizeValueParam.getValue() + "), default value will be used (" + DEFAULT_MAX_FILE_SIZE + ")");
}
}
ValueParam maxPagesValueParam = initParams.getValueParam(MAX_PAGES_PARAM_NAME);
if (maxPagesValueParam != null) {
try {
maxPages = Long.parseLong(maxPagesValueParam.getValue());
} catch (NumberFormatException e) {
LOG.warn("Parameter " + MAX_PAGES_PARAM_NAME + " for document preview is not a valid number ("
+ maxPagesValueParam.getValue() + "), default value will be used (" + MAX_PAGES_PARAM_NAME + ")");
}
}
}
}
public long getMaxFileSize() {
return maxFileSize;
}
public long getMaxPages() {
return maxPages;
}
public ExoCache<Serializable, Object> getCache() {
return pdfCache;
}
/**
* Init pdf document from InputStream in nt:file node
*
* @param currentNode
* @param repoName
* @return
* @throws Exception
*/
public Document initDocument(Node currentNode, String repoName) throws Exception {
return buildDocumentImage(getPDFDocumentFile(currentNode, repoName), currentNode.getName());
}
public Document buildDocumentImage(File input, String name) {
Document document = new Document();
// Turn off Log of org.icepdf.core.pobjects.Document to avoid printing error
// stack trace in case viewing
// a PDF file which use new Public Key Security Handler.
// TODO: Remove this statement after IcePDF fix this
Logger.getLogger(Document.class.toString()).setLevel(Level.OFF);
if (input == null)
return null;
// Capture the page image to file
try {
// cut the file name if name is too long, because OS allows only file with
// name < 250 characters
name = reduceFileNameSize(name);
FileInputStream fis = new FileInputStream(input);
document.setInputStream(new BufferedInputStream(fis), name);
return document;
} catch (Exception ex) {
LOG.warn("Failed to build Document image from pdf file " + name);
if (LOG.isDebugEnabled()) {
LOG.debug(ex);
}
return null;
}
}
/**
* Write PDF data to file
*
* @param currentNode
* @param repoName
* @return
* @throws Exception
*/
public File getPDFDocumentFile(Node currentNode, String repoName) throws Exception {
String wsName = currentNode.getSession().getWorkspace().getName();
String uuid = currentNode.getUUID();
StringBuilder bd = new StringBuilder();
StringBuilder bd1 = new StringBuilder();
StringBuilder bd2 = new StringBuilder();
bd.append(repoName).append("/").append(wsName).append("/").append(uuid);
bd1.append(bd).append("/jcr:lastModified");
bd2.append(bd).append("/jcr:baseVersion");
String path = (String) pdfCache.get(new ObjectKey(bd.toString()));
String lastModifiedTime = (String) pdfCache.get(new ObjectKey(bd1.toString()));
String cachedBaseVersion = (String) pdfCache.get(new ObjectKey(bd2.toString()));
File content = null;
String name = currentNode.getName().replaceAll(":", "_");
Node contentNode = currentNode.getNode("jcr:content");
String lastModified = Utils.getJcrContentLastModified(currentNode);
String baseVersion = Utils.getJcrContentBaseVersion(currentNode);
if (path == null || !(content = new File(path)).exists() || !lastModified.equals(lastModifiedTime) ||
!StringUtils.equals(baseVersion, cachedBaseVersion)) {
String mimeType = contentNode.getProperty("jcr:mimeType").getString();
InputStream input = new BufferedInputStream(contentNode.getProperty("jcr:data").getStream());
// Create temp file to store converted data of nt:file node
if (name.indexOf(".") > 0)
name = name.substring(0, name.lastIndexOf("."));
// cut the file name if name is too long, because OS allows only file with
// name < 250 characters
name = reduceFileNameSize(name);
content = File.createTempFile(name + "_tmp", ".pdf");
// Convert to pdf if need
String extension = DMSMimeTypeResolver.getInstance().getExtension(mimeType);
if ("pdf".equals(extension)) {
read(input, new BufferedOutputStream(new FileOutputStream(content)));
} else {
// create temp file to store original data of nt:file node
File in = File.createTempFile(name + "_tmp", "." + extension);
read(input, new BufferedOutputStream(new FileOutputStream(in)));
long fileSize = in.length(); // size in byte
if (LOG.isDebugEnabled()) {
LOG.debug("File '" + currentNode.getPath() + "' of " + fileSize + " B. Size limit for preview: "
+ (getMaxFileSize() / (1024 * 1024)) + " MB");
}
if (fileSize <= getMaxFileSize()) {
try {
boolean success = jodConverter_.convert(in, content, "pdf");
// If the converting failed then delete the content of temporary
// file
if (!success) {
content.delete();
content = null;
}
} catch (OfficeException connection) {
content.delete();
content = null;
if (LOG.isErrorEnabled()) {
LOG.error("Exception when using Office Service", connection);
}
} finally {
in.delete();
}
} else {
LOG.info("File '" + currentNode.getPath() + "' is too big for preview.");
content.delete();
content = null;
in.delete();
}
}
if (content != null && content.exists()) {
if (contentNode.hasProperty("jcr:lastModified")) {
pdfCache.put(new ObjectKey(bd.toString()), content.getPath());
pdfCache.put(new ObjectKey(bd1.toString()), lastModified);
}
pdfCache.put(new ObjectKey(bd2.toString()), baseVersion);
}
}
return content;
}
/**
* reduces the file name size. If the length is > 150, return the first 150
* characters, else, return the original value
*
* @param name the name
* @return the reduced name
*/
private String reduceFileNameSize(String name) {
return (name != null && name.length() > MAX_NAME_LENGTH) ? name.substring(0, MAX_NAME_LENGTH) : name;
}
private void read(InputStream is, OutputStream os) throws Exception {
int bufferLength = 1024;
int readLength = 0;
while (readLength > -1) {
byte[] chunk = new byte[bufferLength];
readLength = is.read(chunk);
if (readLength > 0) {
os.write(chunk, 0, readLength);
}
}
os.flush();
os.close();
}
}