org.nuxeo.ecm.platform.pictures.tiles.restlets.PictureTilesRestlets.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.platform.pictures.tiles.restlets.PictureTilesRestlets.java

Source

/*
 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     Nuxeo - initial API and implementation
 *
 * $Id$
 *
 */
package org.nuxeo.ecm.platform.pictures.tiles.restlets;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.nuxeo.common.utils.FileUtils;
import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.NuxeoException;
import org.nuxeo.ecm.core.io.download.DownloadService;
import org.nuxeo.ecm.platform.pictures.tiles.api.PictureTiles;
import org.nuxeo.ecm.platform.pictures.tiles.api.adapter.PictureTilesAdapter;
import org.nuxeo.ecm.platform.pictures.tiles.serializer.JSONPictureTilesSerializer;
import org.nuxeo.ecm.platform.pictures.tiles.serializer.PictureTilesSerializer;
import org.nuxeo.ecm.platform.pictures.tiles.serializer.XMLPictureTilesSerializer;
import org.nuxeo.ecm.platform.ui.web.restAPI.BaseStatelessNuxeoRestlet;
import org.nuxeo.runtime.api.Framework;
import org.restlet.data.CharacterSet;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;

/**
 * Restlet to provide a REST API on top of the PictureTilingService.
 *
 * @author tiry
 */
public class PictureTilesRestlets extends BaseStatelessNuxeoRestlet {

    // cache duration in seconds
    protected static int MAX_CACHE_LIFE = 60 * 10;

    protected static Map<String, PictureTilesCachedEntry> cachedAdapters = new ConcurrentHashMap<String, PictureTilesCachedEntry>();

    @Override
    public void handle(Request req, Response res) {
        HttpServletRequest request = getHttpRequest(req);
        HttpServletResponse response = getHttpResponse(res);

        String repo = (String) req.getAttributes().get("repoId");
        String docid = (String) req.getAttributes().get("docId");
        Integer tileWidth = Integer.decode((String) req.getAttributes().get("tileWidth"));
        Integer tileHeight = Integer.decode((String) req.getAttributes().get("tileHeight"));
        Integer maxTiles = Integer.decode((String) req.getAttributes().get("maxTiles"));

        Form form = req.getResourceRef().getQueryAsForm();
        String xpath = (String) form.getFirstValue("fieldPath");
        String x = form.getFirstValue("x");
        String y = form.getFirstValue("y");
        String format = form.getFirstValue("format");

        String test = form.getFirstValue("test");
        if (test != null) {
            try {
                handleSendTest(res, repo, docid, tileWidth, tileHeight, maxTiles);
                return;
            } catch (IOException e) {
                handleError(res, e);
                return;
            }
        }

        if (repo == null || repo.equals("*")) {
            handleError(res, "you must specify a repository");
            return;
        }
        if (docid == null || repo.equals("*")) {
            handleError(res, "you must specify a documentId");
            return;
        }
        Boolean init = initRepositoryAndTargetDocument(res, repo, docid);

        if (!init) {
            handleError(res, "unable to init repository connection");
            return;
        }

        PictureTilesAdapter adapter;
        try {
            adapter = getFromCache(targetDocument, xpath);
            if (adapter == null) {
                adapter = targetDocument.getAdapter(PictureTilesAdapter.class);
                if ((xpath != null) && (!"".equals(xpath))) {
                    adapter.setXPath(xpath);
                }
                updateCache(targetDocument, adapter, xpath);
            }
        } catch (NuxeoException e) {
            handleError(res, e);
            return;
        }

        if (adapter == null) {
            handleNoTiles(res, null);
            return;
        }

        PictureTiles tiles = null;
        try {
            tiles = adapter.getTiles(tileWidth, tileHeight, maxTiles);
        } catch (NuxeoException e) {
            handleError(res, e);
        }

        if ((x == null) || (y == null)) {
            handleSendInfo(res, tiles, format);
            return;
        }

        final Blob image;
        try {
            image = tiles.getTile(Integer.decode(x), Integer.decode(y));
        } catch (NuxeoException | IOException e) {
            handleError(res, e);
            return;
        }

        String reason = "tile";
        Boolean inline = Boolean.TRUE;
        Map<String, Serializable> extendedInfos = new HashMap<>();
        extendedInfos.put("x", x);
        extendedInfos.put("y", y);
        DownloadService downloadService = Framework.getService(DownloadService.class);
        try {
            downloadService.downloadBlob(request, response, targetDocument, xpath, image, image.getFilename(),
                    reason, extendedInfos, inline, byteRange -> setEntityToBlobOutput(image, byteRange, res));
        } catch (IOException e) {
            handleError(res, e);
        }
    }

    protected void handleSendTest(Response res, String repoId, String docId, Integer tileWidth, Integer tileHeight,
            Integer maxTiles) throws IOException {
        MediaType mt = null;
        mt = MediaType.TEXT_HTML;

        File file = FileUtils.getResourceFileFromContext("testTiling.html");
        String html = org.apache.commons.io.FileUtils.readFileToString(file);

        html = html.replace("$repoId$", repoId);
        html = html.replace("$docId$", docId);
        html = html.replace("$tileWidth$", tileWidth.toString());
        html = html.replace("$tileHeight$", tileHeight.toString());
        html = html.replace("$maxTiles$", maxTiles.toString());

        res.setEntity(html, mt);
    }

    protected void handleSendInfo(Response res, PictureTiles tiles, String format) {
        if (format == null) {
            format = "XML";
        }
        MediaType mt = null;
        PictureTilesSerializer serializer = null;

        if (format.equalsIgnoreCase("json")) {
            serializer = new JSONPictureTilesSerializer();
            mt = MediaType.APPLICATION_JSON;
        } else {
            serializer = new XMLPictureTilesSerializer();
            mt = MediaType.TEXT_XML;
        }

        res.setEntity(serializer.serialize(tiles), mt);
        res.getEntity().setCharacterSet(CharacterSet.UTF_8);

        HttpServletResponse response = getHttpResponse(res);
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
    }

    protected void handleNoTiles(Response res, Exception e) {
        StringBuilder sb = new StringBuilder();

        sb.append("<html><body><center><h1>");
        if (e == null) {
            sb.append("No Tiling is available for this document</h1>");
        } else {
            sb.append("Picture Tiling can not be generated for this document</h1>");
            sb.append("<br/><pre>");
            sb.append(e.toString());
            sb.append("</pre>");
        }

        sb.append("</center></body></html>");

        res.setEntity(sb.toString(), MediaType.TEXT_HTML);
        HttpServletResponse response = getHttpResponse(res);
        response.setHeader("Content-Disposition", "inline");
    }

    protected void updateCache(DocumentModel doc, PictureTilesAdapter adapter, String xpath) {

        Calendar modified = (Calendar) doc.getProperty("dublincore", "modified");
        PictureTilesCachedEntry entry = new PictureTilesCachedEntry(modified, adapter, xpath);
        synchronized (cachedAdapters) {
            cachedAdapters.put(doc.getId(), entry);
        }
        cacheGC();
    }

    protected void removeFromCache(String key) {
        PictureTilesCachedEntry entry = cachedAdapters.get(key);
        if (entry != null) {
            entry.getAdapter().cleanup();
        }
        synchronized (cachedAdapters) {
            cachedAdapters.remove(key);
        }
    }

    protected boolean isSameDate(Calendar d1, Calendar d2) {

        // because one of the date is stored in the repository
        // the date may be 'rounded'
        // so compare
        long t1 = d1.getTimeInMillis() / 1000;
        long t2 = d2.getTimeInMillis() / 1000;
        return Math.abs(t1 - t2) <= 1;
    }

    protected PictureTilesAdapter getFromCache(DocumentModel doc, String xpath) {
        if (cachedAdapters.containsKey(doc.getId())) {
            if (xpath == null) {
                xpath = "";
            }
            Calendar modified = (Calendar) doc.getProperty("dublincore", "modified");
            PictureTilesCachedEntry entry = cachedAdapters.get(doc.getId());

            if ((!isSameDate(entry.getModified(), modified)) || (!xpath.equals(entry.getXpath()))) {
                removeFromCache(doc.getId());
                return null;
            } else {
                return entry.getAdapter();
            }
        } else {
            return null;
        }
    }

    protected void cacheGC() {
        for (String key : cachedAdapters.keySet()) {
            long now = System.currentTimeMillis();
            PictureTilesCachedEntry entry = cachedAdapters.get(key);
            if ((now - entry.getTimeStamp()) > MAX_CACHE_LIFE * 1000) {
            }
        }
    }

}