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