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        private static final long serialVersionUID = 1L;
055
056        public ThrowableSerializer(BeanSerializer src) {
057            super(src);
058        }
059
060        @Override
061        protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider)
062                throws IOException, JsonGenerationException {
063            serializeClassName(bean, jgen, provider);
064            super.serializeFields(bean, jgen, provider);
065        }
066
067        @Override
068        protected void serializeFieldsFiltered(Object bean, JsonGenerator jgen, SerializerProvider provider)
069                throws IOException, JsonGenerationException {
070            serializeClassName(bean, jgen, provider);
071            super.serializeFieldsFiltered(bean, jgen, provider);
072        }
073
074        protected void serializeClassName(Object bean, JsonGenerator jgen, SerializerProvider provider)
075                throws JsonGenerationException, IOException {
076            jgen.writeFieldName("className");
077            jgen.writeString(bean.getClass().getName());
078        }
079    }
080
081    private JsonFactory factory;
082
083    @Override
084    public JsonFactory getJsonFactory() {
085        if (factory == null) {
086            factory = createFactory();
087        }
088        return factory;
089    }
090
091    @Override
092    public JsonFactory createFactory() {
093        JsonFactory factory = new JsonFactory();
094        final ObjectMapper oc = new ObjectMapper(factory);
095        final SimpleModule module = new SimpleModule("webengine", Version.unknownVersion()) {
096
097            private static final long serialVersionUID = 1L;
098
099            @Override
100            public void setupModule(SetupContext context) {
101                super.setupModule(context);
102
103                context.addBeanSerializerModifier(new BeanSerializerModifier() {
104
105                    @Override
106                    public JsonSerializer<?> modifySerializer(SerializationConfig config,
107                            BeanDescription beanDesc, JsonSerializer<?> serializer) {
108                        if (!Throwable.class.isAssignableFrom(beanDesc.getBeanClass())) {
109                            return super.modifySerializer(config, beanDesc, serializer);
110                        }
111                        return new ThrowableSerializer((BeanSerializer) serializer);
112                    }
113                });
114            }
115        };
116        oc.registerModule(module);
117        factory.setCodec(oc);
118        return factory;
119    }
120
121    @Override
122    public boolean toggleStackDisplay() {
123        return stackDisplay = !stackDisplay;
124    }
125
126    @Override
127    public boolean isStackDisplay() {
128        return stackDisplay;
129    }
130}