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 *     Nicolas Chapurlat <nchapurlat@nuxeo.com>
016 */
017
018package org.nuxeo.ecm.webengine.jaxrs.coreiodelegate;
019
020import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
021import static org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonReader.LEGACY_MODE_READER;
022import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON;
023import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.DERIVATIVE;
024
025import java.io.IOException;
026import java.io.InputStream;
027import java.lang.reflect.InvocationTargetException;
028import java.lang.reflect.Method;
029import java.lang.reflect.Type;
030
031import javax.servlet.http.HttpServletRequest;
032import javax.ws.rs.core.MediaType;
033import javax.ws.rs.core.MultivaluedMap;
034
035import org.apache.commons.logging.Log;
036import org.apache.commons.logging.LogFactory;
037import org.codehaus.jackson.JsonParser;
038import org.nuxeo.ecm.core.api.DocumentModel;
039import org.nuxeo.ecm.core.io.marshallers.json.JsonFactoryProvider;
040import org.nuxeo.ecm.core.io.registry.Reader;
041import org.nuxeo.ecm.core.io.registry.context.RenderingContext;
042import org.nuxeo.ecm.core.io.registry.reflect.Setup;
043import org.nuxeo.ecm.core.io.registry.reflect.Supports;
044import org.nuxeo.runtime.api.Framework;
045
046/**
047 * Delegates the {@link DocumentModel} Json reading to the old marshaller: JSONDocumentModelReader.
048 * <p>
049 * It's enable if system property nuxeo.document.json.legacy=true or if request header X-NXDocumentJsonLegacy=true.
050 * </p>
051 *
052 * @since 7.2
053 */
054@Setup(mode = SINGLETON, priority = DERIVATIVE)
055@Supports(APPLICATION_JSON)
056public class DocumentModelJsonReaderLegacy implements Reader<DocumentModel> {
057
058    private static final Log log = LogFactory.getLog(DocumentModelJsonReaderLegacy.class);
059
060    public static final String CONF_DOCUMENT_JSON_LEGACY = "nuxeo.document.json.legacy";
061
062    public static final String HEADER_DOCUMENT_JSON_LEGACY = "X-NXDocumentJsonLegacy";
063
064    private static boolean IS_METHOD_LOADED = false;
065
066    private static Method METHOD = null;
067
068    private static void loadMethod() {
069        try {
070            Class<?> legacy = Class.forName("org.nuxeo.ecm.automation.jaxrs.io.documents.JSONDocumentModelReader");
071            Method method = legacy.getMethod("readJson", JsonParser.class, MultivaluedMap.class,
072                    HttpServletRequest.class);
073            METHOD = method;
074        } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) {
075            log.error(
076                    "Unable to find method org.nuxeo.ecm.automation.jaxrs.io.documents.JSONDocumentModelReader.readJson(JsonParser, MultivaluedMap<String, String>, HttpServletRequest)",
077                    e);
078            return;
079        }
080    }
081
082    private static Boolean CONF_KEY = null;
083
084    private static boolean getConfKey() {
085        if (CONF_KEY == null) {
086            CONF_KEY = Framework.isBooleanPropertyTrue(CONF_DOCUMENT_JSON_LEGACY);
087        }
088        return CONF_KEY;
089    }
090
091    public static void pushInstanceIfNeeded(RenderingContext ctx, HttpServletRequest request,
092            MultivaluedMap<String, String> httpHeaders) {
093        if (!IS_METHOD_LOADED) {
094            loadMethod();
095        }
096        if (METHOD == null) {
097            return;
098        }
099        String header = request.getHeader(HEADER_DOCUMENT_JSON_LEGACY);
100        if (header != null) {
101            try {
102                boolean enable = Boolean.valueOf(header);
103                if (enable) {
104                    DocumentModelJsonReaderLegacy instance = new DocumentModelJsonReaderLegacy(request, httpHeaders);
105                    ctx.setParameterValues(LEGACY_MODE_READER, instance);
106                    return;
107                } else {
108                    return;
109                }
110            } catch (Exception e) {
111                log.warn("Invalid header value for X-NXDocumentJsonLegacy : true|false");
112            }
113        }
114        if (getConfKey()) {
115            DocumentModelJsonReaderLegacy instance = new DocumentModelJsonReaderLegacy(request, httpHeaders);
116            ctx.setParameterValues(LEGACY_MODE_READER, instance);
117            return;
118        } else {
119            return;
120        }
121    }
122
123    private HttpServletRequest request;
124
125    private MultivaluedMap<String, String> httpHeaders;
126
127    private DocumentModelJsonReaderLegacy(HttpServletRequest request, MultivaluedMap<String, String> httpHeaders) {
128        super();
129        this.request = request;
130        this.httpHeaders = httpHeaders;
131    }
132
133    @Override
134    public boolean accept(Class<?> clazz, Type genericType, MediaType mediatype) {
135        return true;
136    }
137
138    @Override
139    public DocumentModel read(Class<?> clazz, Type genericType, MediaType mediaType, InputStream in) throws IOException {
140        try {
141            JsonParser parser = JsonFactoryProvider.get().createJsonParser(in);
142            return DocumentModel.class.cast(METHOD.invoke(null, parser, httpHeaders, request));
143        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
144            log.error("Unable to use legacy document model reading", e);
145            return null;
146        }
147    }
148
149}