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