001/*
002 * (C) Copyright 2014 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 *     dmetzler
018 *     Vladimir Pasquir <vpasquier@nuxeo.com>
019 */
020package org.nuxeo.ecm.webengine;
021
022import java.io.IOException;
023
024import org.nuxeo.runtime.api.Framework;
025
026import com.fasterxml.jackson.core.JsonFactory;
027import com.fasterxml.jackson.core.JsonGenerationException;
028import com.fasterxml.jackson.core.JsonGenerator;
029import com.fasterxml.jackson.core.Version;
030import com.fasterxml.jackson.databind.BeanDescription;
031import com.fasterxml.jackson.databind.JsonSerializer;
032import com.fasterxml.jackson.databind.ObjectMapper;
033import com.fasterxml.jackson.databind.SerializationConfig;
034import com.fasterxml.jackson.databind.SerializerProvider;
035import com.fasterxml.jackson.databind.module.SimpleModule;
036import com.fasterxml.jackson.databind.ser.BeanSerializer;
037import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
038
039/**
040 * @since 6.0
041 */
042public class JsonFactoryManagerImpl implements JsonFactoryManager {
043
044    public static final String REST_STACK_DISPLAY = "org.nuxeo.rest.stack" + ".enable";
045
046    protected boolean stackDisplay;
047
048    public JsonFactoryManagerImpl() {
049        stackDisplay = Framework.isBooleanPropertyTrue(REST_STACK_DISPLAY);
050    }
051
052    private static class ThrowableSerializer extends BeanSerializer {
053
054        public ThrowableSerializer(BeanSerializer src) {
055            super(src);
056        }
057
058        @Override
059        protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider)
060                throws IOException, JsonGenerationException {
061            serializeClassName(bean, jgen, provider);
062            super.serializeFields(bean, jgen, provider);
063        }
064
065        @Override
066        protected void serializeFieldsFiltered(Object bean, JsonGenerator jgen, SerializerProvider provider)
067                throws IOException, JsonGenerationException {
068            serializeClassName(bean, jgen, provider);
069            super.serializeFieldsFiltered(bean, jgen, provider);
070        }
071
072        protected void serializeClassName(Object bean, JsonGenerator jgen, SerializerProvider provider)
073                throws JsonGenerationException, IOException {
074            jgen.writeFieldName("className");
075            jgen.writeString(bean.getClass().getName());
076        }
077    }
078
079    private JsonFactory factory;
080
081    @Override
082    public JsonFactory getJsonFactory() {
083        if (factory == null) {
084            factory = createFactory();
085        }
086        return factory;
087    }
088
089    @Override
090    public JsonFactory createFactory() {
091        JsonFactory factory = new JsonFactory();
092        final ObjectMapper oc = new ObjectMapper(factory);
093        final SimpleModule module = new SimpleModule("webengine", Version.unknownVersion()) {
094
095            @Override
096            public void setupModule(SetupContext context) {
097                super.setupModule(context);
098
099                context.addBeanSerializerModifier(new BeanSerializerModifier() {
100
101                    @Override
102                    public JsonSerializer<?> modifySerializer(SerializationConfig config,
103                            BeanDescription beanDesc, JsonSerializer<?> serializer) {
104                        if (!Throwable.class.isAssignableFrom(beanDesc.getBeanClass())) {
105                            return super.modifySerializer(config, beanDesc, serializer);
106                        }
107                        return new ThrowableSerializer((BeanSerializer) serializer);
108                    }
109                });
110            }
111        };
112        oc.registerModule(module);
113        factory.setCodec(oc);
114        return factory;
115    }
116
117    @Override
118    public boolean toggleStackDisplay() {
119        return stackDisplay = !stackDisplay;
120    }
121
122    public boolean isStackDisplay() {
123        return stackDisplay;
124    }
125}