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.core.io.marshallers.json;
021
022import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
023import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
024
025import java.io.IOException;
026import java.io.OutputStream;
027import java.lang.reflect.Type;
028
029import javax.inject.Inject;
030import javax.ws.rs.core.MediaType;
031
032import org.codehaus.jackson.JsonGenerator;
033import org.nuxeo.ecm.core.io.registry.MarshallerRegistry;
034import org.nuxeo.ecm.core.io.registry.MarshallingException;
035import org.nuxeo.ecm.core.io.registry.Writer;
036import org.nuxeo.ecm.core.io.registry.context.RenderingContext;
037import org.nuxeo.ecm.core.io.registry.reflect.Supports;
038
039/**
040 * Base class for Json {@link Writer}.
041 * <p>
042 * This class provides a easy way to produce json and also provides the current context: {@link AbstractJsonWriter#ctx}.
043 * It provides you a {@link JsonGenerator} to manage the marshalling.
044 * </p>
045 * <p>
046 * The use of this class optimize the JsonFactory usage especially when aggregating marshallers.
047 * </p>
048 *
049 * @param <EntityType> The Java type to marshall as Json.
050 * @since 7.2
051 */
052@Supports(APPLICATION_JSON)
053public abstract class AbstractJsonWriter<EntityType> implements Writer<EntityType> {
054
055    /**
056     * The current {@link RenderingContext}.
057     */
058    @Inject
059    protected RenderingContext ctx;
060
061    /**
062     * The marshaller registry.
063     */
064    @Inject
065    protected MarshallerRegistry registry;
066
067    @Override
068    public boolean accept(Class<?> clazz, Type genericType, MediaType mediatype) {
069        return true;
070    }
071
072    @Override
073    public void write(EntityType entity, Class<?> clazz, Type genericType, MediaType mediatype, OutputStream out)
074            throws IOException {
075        JsonGenerator jg = getGenerator(out, true);
076        write(entity, jg);
077        jg.flush();
078    }
079
080    /**
081     * Implement this method to writes the entity in the provided {@link JsonGenerator}.
082     * <p>
083     * This method implementation can use injected properties.
084     * </p>
085     * <p>
086     * The {@link JsonGenerator}'s flushing is done by this abstract class, it's also not not necessary to flush it. Do
087     * not close the provided {@link JsonGenerator}. It may be used is another marshaller calling this one.
088     * </p>
089     *
090     * @param entity The entity to marshall as Json.
091     * @param jg The {@link JsonGenerator} used to produce Json output.
092     * @since 7.2
093     */
094    public abstract void write(EntityType entity, JsonGenerator jg) throws IOException;
095
096    /**
097     * Delegates writing of an entity to the {@link MarshallerRegistry}. This will work if a Json {@link Writer} is
098     * registered in the registry for the given clazz.
099     *
100     * @param fieldName The name of the Json field in which the entity will be wrote.
101     * @param entity The entity to write.
102     * @param jg The {@link JsonGenerator} used to write the given entity.
103     * @since 7.2
104     */
105    protected void writeEntityField(String fieldName, Object entity, JsonGenerator jg) throws IOException {
106        jg.writeFieldName(fieldName);
107        writeEntity(entity, jg);
108    }
109
110    /**
111     * Delegates writing of an entity to the {@link MarshallerRegistry}. This will work if a Json {@link Writer} is
112     * registered in the registry for the given clazz.
113     *
114     * @param entity The entity to write.
115     * @param jg The {@link JsonGenerator} used to write the given entity.
116     * @since 7.2
117     */
118    protected void writeEntity(Object entity, JsonGenerator jg) throws IOException {
119        writeEntity(entity, new OutputStreamWithJsonWriter(jg));
120    }
121
122    /**
123     * Delegates writing of an entity to the {@link MarshallerRegistry}. This will work if a Json {@link Writer} is
124     * registered in the registry for the given clazz.
125     *
126     * @param entity The entity to write.
127     * @param out The {@link OutputStream} in which the given entity will be wrote.
128     * @throws IOException If some i/o error append while writing entity.
129     * @since 7.2
130     */
131    protected <ObjectType> void writeEntity(ObjectType entity, OutputStream out) throws IOException {
132        @SuppressWarnings("unchecked")
133        Class<ObjectType> clazz = (Class<ObjectType>) entity.getClass();
134        Writer<ObjectType> writer = registry.getWriter(ctx, clazz, APPLICATION_JSON_TYPE);
135        if (writer == null) {
136            throw new MarshallingException("Unable to get a writer for Java type " + entity.getClass()
137                    + " and mimetype " + APPLICATION_JSON_TYPE);
138        }
139        writer.write(entity, entity.getClass(), entity.getClass(), APPLICATION_JSON_TYPE, out);
140    }
141
142    /**
143     * Get the current Json generator or create it if none was found.
144     *
145     * @param out The {@link OutputStream} on which the generator will generate Json.
146     * @param getCurrentIfAvailable If true, try to get the current generator in the context.
147     * @return The created generator.
148     * @since 7.2
149     */
150    protected JsonGenerator getGenerator(OutputStream out, boolean getCurrentIfAvailable) throws IOException {
151        if (getCurrentIfAvailable && out instanceof OutputStreamWithJsonWriter) {
152            OutputStreamWithJsonWriter casted = (OutputStreamWithJsonWriter) out;
153            return casted.getJsonGenerator();
154        }
155        return JsonFactoryProvider.get().createJsonGenerator(out);
156    }
157
158}