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