001/* 002 * (C) Copyright 2015 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 * Contributors: 017 * thibaud 018 */ 019package org.nuxeo.diff.pictures; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.io.Serializable; 025import java.util.HashMap; 026 027import javax.servlet.ServletException; 028import javax.servlet.http.HttpServlet; 029import javax.servlet.http.HttpServletRequest; 030import javax.servlet.http.HttpServletResponse; 031 032import org.apache.commons.lang.StringUtils; 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.nuxeo.ecm.core.api.Blob; 036import org.nuxeo.ecm.core.api.CoreInstance; 037import org.nuxeo.ecm.core.api.CoreSession; 038import org.nuxeo.ecm.core.api.DocumentModel; 039import org.nuxeo.ecm.core.api.IdRef; 040import org.nuxeo.ecm.core.api.NuxeoException; 041import org.nuxeo.ecm.core.api.repository.RepositoryManager; 042import org.nuxeo.ecm.platform.commandline.executor.api.CommandNotAvailable; 043import org.nuxeo.ecm.platform.web.common.ServletHelper; 044import org.nuxeo.runtime.api.Framework; 045import org.nuxeo.runtime.transaction.TransactionHelper; 046 047/** 048 * /nuxeo/diffPictures?&repo=therepo&leftDocId=123456&rightDocId= 456789012 049 * &xpath=file:content&commandLine=thecommandline&fuzz=1000&highlightColor=Red &lowlightColor=White&altExtension=jpg 050 * <p> 051 * <ul> 052 * <li><code>leftDocId</code> and <code>rightDocId</code> are required</li> 053 * <li>All other parameters are optional (commandLine, xpath, fuzz, ...). Default values are defined in 054 * <code>DiffPictures</code></li> 055 * <li><code>altExtension</code> is special. If the pictures to compare are _not_ jpeg, png, or gif, _and_ if this 056 * parameter is set, then the result picture will be of this kind. Useful when comparing 2 psd or tif files for example, 057 * and the browser can't display them</li> 058 * </ul> 059 * <p> 060 * commandline, xpath, fuzz, highlightColor, lowlightColor and repo are optional 061 * 062 * @since 7.4 063 */ 064public class DiffPicturesServlet extends HttpServlet { 065 066 private static final long serialVersionUID = 1L; 067 068 private static final Log log = LogFactory.getLog(DiffPicturesServlet.class); 069 070 protected static final int BUFFER_SIZE = 1024 * 512; 071 072 @Override 073 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 074 075 String leftDocId = req.getParameter("leftDocId"); 076 String rightDocId = req.getParameter("rightDocId"); 077 078 if (StringUtils.isBlank(leftDocId)) { 079 sendTextResponse(resp, "you must specify a left document as origin"); 080 return; 081 } 082 if (StringUtils.isBlank(rightDocId)) { 083 sendTextResponse(resp, "you must specify 'right' used for comparison against the left document."); 084 return; 085 } 086 087 TransactionHelper.runInTransaction(() -> { 088 089 // WARNING: If you change the name of a parameter, also change it in nuxeo-diff-pictures.js 090 String repo = req.getParameter("repo"); 091 String xpath = req.getParameter("xpath"); 092 String commandLine = req.getParameter("commandLine"); 093 String fuzz = req.getParameter("fuzz"); 094 String highlightColor = req.getParameter("highlightColor"); 095 String lowlightColor = req.getParameter("lowlightColor"); 096 String altExtension = req.getParameter("altExtension"); 097 098 if (StringUtils.isBlank(repo)) { 099 repo = Framework.getLocalService(RepositoryManager.class).getDefaultRepository().getName(); 100 } 101 102 // This try-with-resources does an implicit close() at the end 103 try (CoreSession coreSession = CoreInstance.openCoreSession(repo)) { 104 105 DocumentModel leftDoc = coreSession.getDocument(new IdRef(leftDocId)); 106 DocumentModel rightDoc = coreSession.getDocument(new IdRef(rightDocId)); 107 108 DiffPictures dp = new DiffPictures(leftDoc, rightDoc, xpath); 109 110 HashMap<String, Serializable> params = new HashMap<String, Serializable>(); 111 if (StringUtils.isNotBlank(fuzz)) { 112 params.put("fuzz", fuzz); 113 } 114 if (StringUtils.isNotBlank(highlightColor)) { 115 params.put("highlightColor", highlightColor); 116 } 117 if (StringUtils.isNotBlank(lowlightColor)) { 118 params.put("lowlightColor", lowlightColor); 119 } 120 121 if (StringUtils.isNotBlank(altExtension)) { 122 // Using the leftDoc only 123 Blob leftB; 124 if (StringUtils.isBlank(xpath) || "null".equals(xpath)) { 125 leftB = (Blob) leftDoc.getPropertyValue(DiffPictures.DEFAULT_XPATH); 126 } else { 127 leftB = (Blob) leftDoc.getPropertyValue(xpath); 128 } 129 String fileName = leftB.getFilename(); 130 int dotPos = fileName.lastIndexOf("."); 131 String ext = fileName.substring(dotPos + 1); 132 ext = ext.toLowerCase(); 133 switch (ext) { 134 case "jpg": 135 case "jpeg": 136 case "png": 137 case "gif": 138 // No need to change anything 139 break; 140 141 default: 142 if (altExtension.indexOf(".") != 0) { 143 altExtension = "." + altExtension; 144 } 145 fileName = "comp-" + fileName + altExtension; 146 params.put("targetFileName", fileName); 147 break; 148 149 } 150 } 151 152 Blob bResult; 153 try { 154 bResult = dp.compare(commandLine, params); 155 } catch (CommandNotAvailable | IOException e) { 156 log.error("Unable to compare the pictures", e); 157 try { 158 sendTextResponse(resp, "Unable to compare the pictures"); 159 } catch (IOException e1) { 160 throw new NuxeoException(e1); 161 } 162 return; 163 } 164 165 resp.setHeader("Cache-Control", "no-cache"); 166 resp.setHeader("Pragma", "no-cache"); 167 try { 168 sendBlobResult(req, resp, bResult); 169 } catch (IOException e) { 170 log.error("Unable to handleCompareResult", e); 171 try { 172 sendTextResponse(resp, "Unable to return the result"); 173 } catch (IOException e1) { 174 throw new NuxeoException(e1); 175 } 176 return; 177 } 178 } 179 }); 180 } 181 182 protected void sendTextResponse(HttpServletResponse resp, String response) throws IOException { 183 184 resp.setContentType("text/plain"); 185 resp.setContentLength(response.getBytes().length); 186 OutputStream out = resp.getOutputStream(); 187 out.write(response.getBytes()); 188 out.close(); 189 190 } 191 192 protected void sendBlobResult(HttpServletRequest req, HttpServletResponse resp, Blob blob) throws IOException { 193 194 InputStream in = blob.getStream(); 195 OutputStream out = resp.getOutputStream(); 196 String fileName = blob.getFilename(); 197 198 resp.setHeader("Content-Disposition", ServletHelper.getRFC2231ContentDisposition(req, fileName)); 199 resp.setContentType(blob.getMimeType()); 200 long fileSize = blob.getLength(); 201 resp.setContentLength((int) fileSize); 202 203 byte[] buffer = new byte[BUFFER_SIZE]; 204 int bytesRead; 205 while ((bytesRead = in.read(buffer)) != -1) { 206 out.write(buffer, 0, bytesRead); 207 } 208 209 } 210}