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