001/*
002 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     bstefanescu
016 */
017package org.nuxeo.ecm.automation.jaxrs.io;
018
019import java.io.ByteArrayOutputStream;
020import java.io.IOException;
021import java.io.OutputStream;
022import java.util.Arrays;
023import java.util.List;
024
025import org.codehaus.jackson.JsonEncoding;
026import org.codehaus.jackson.JsonFactory;
027import org.codehaus.jackson.JsonGenerator;
028import org.nuxeo.ecm.automation.AutomationService;
029import org.nuxeo.ecm.automation.OperationDocumentation;
030import org.nuxeo.ecm.automation.OperationDocumentation.Param;
031import org.nuxeo.ecm.automation.OperationException;
032import org.nuxeo.ecm.automation.core.Constants;
033import org.nuxeo.ecm.automation.io.services.codec.ObjectCodec;
034import org.nuxeo.ecm.automation.io.services.codec.ObjectCodecService;
035import org.nuxeo.ecm.automation.jaxrs.LoginInfo;
036import org.nuxeo.ecm.automation.jaxrs.io.operations.AutomationInfo;
037import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition;
038import org.nuxeo.ecm.platform.forms.layout.io.JSONLayoutExporter;
039import org.nuxeo.ecm.webengine.JsonFactoryManager;
040import org.nuxeo.ecm.webengine.WebException;
041import org.nuxeo.runtime.api.Framework;
042
043/**
044 * Json writer for operations export.
045 *
046 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
047 * @author <a href="mailto:grenard@nuxeo.com">Guillaume Renard</a>
048 */
049public class JsonWriter {
050
051    private static JsonFactory getFactory() {
052        return Framework.getLocalService(JsonFactoryManager.class).getJsonFactory();
053    }
054
055    private static JsonGenerator createGenerator(OutputStream out) throws IOException {
056        return getFactory().createJsonGenerator(out, JsonEncoding.UTF8);
057    }
058
059    public static void writeAutomationInfo(OutputStream out, AutomationInfo info, boolean prettyPrint)
060            throws IOException {
061        writeAutomationInfo(createGenerator(out), info, prettyPrint);
062    }
063
064    public static void writeAutomationInfo(JsonGenerator jg, AutomationInfo info, boolean prettyPrint)
065            throws IOException {
066        if (prettyPrint) {
067            jg.useDefaultPrettyPrinter();
068        }
069        jg.writeStartObject();
070        writePaths(jg);
071        writeCodecs(jg);
072        writeOperations(jg, info);
073        writeChains(jg, info);
074        jg.writeEndObject();
075        jg.flush();
076    }
077
078    private static void writePaths(JsonGenerator jg) throws IOException {
079        jg.writeObjectFieldStart("paths");
080        jg.writeStringField("login", "login");
081        jg.writeEndObject();
082    }
083
084    private static void writeCodecs(JsonGenerator jg) throws IOException {
085        jg.writeArrayFieldStart("codecs");
086        ObjectCodecService codecs = Framework.getLocalService(ObjectCodecService.class);
087        for (ObjectCodec<?> codec : codecs.getCodecs()) {
088            if (!codec.isBuiltin()) {
089                jg.writeString(codec.getClass().getName());
090            }
091        }
092        jg.writeEndArray();
093    }
094
095    /**
096     * Used to export operations to studio.
097     */
098    public static String exportOperations() throws IOException, OperationException {
099        return exportOperations(false);
100    }
101
102    /**
103     * Used to export operations to studio.
104     *
105     * @param filterNotInStudio if true, operation types not exposed in Studio will be filtered.
106     * @since 5.9.1
107     */
108    public static String exportOperations(boolean filterNotInStudio) throws IOException, OperationException {
109        List<OperationDocumentation> ops = Framework.getLocalService(AutomationService.class).getDocumentation();
110        ByteArrayOutputStream out = new ByteArrayOutputStream();
111        JsonGenerator jg = getFactory().createJsonGenerator(out);
112        jg.useDefaultPrettyPrinter();
113        jg.writeStartObject();
114        jg.writeArrayFieldStart("operations");
115        for (OperationDocumentation op : ops) {
116            if (filterNotInStudio) {
117                if (op.addToStudio && !Constants.CAT_CHAIN.equals(op.category)) {
118                    writeOperation(jg, op);
119                }
120            } else {
121                writeOperation(jg, op);
122            }
123        }
124        jg.writeEndArray();
125        jg.writeEndObject();
126        jg.flush();
127        return out.toString("UTF-8");
128    }
129
130    private static void writeOperations(JsonGenerator jg, AutomationInfo info) throws IOException {
131        jg.writeArrayFieldStart("operations");
132        for (OperationDocumentation op : info.getOperations()) {
133            writeOperation(jg, op);
134        }
135        jg.writeEndArray();
136    }
137
138    private static void writeChains(JsonGenerator jg, AutomationInfo info) throws IOException {
139        jg.writeArrayFieldStart("chains");
140        for (OperationDocumentation op : info.getChains()) {
141            writeOperation(jg, op, Constants.CHAIN_ID_PREFIX + op.id);
142        }
143        jg.writeEndArray();
144    }
145
146    public static void writeOperation(OutputStream out, OperationDocumentation op) throws IOException {
147        writeOperation(out, op, false);
148    }
149
150    /**
151     * @since 5.9.4
152     */
153    public static void writeOperation(OutputStream out, OperationDocumentation op, boolean prettyPrint)
154            throws IOException {
155        writeOperation(createGenerator(out), op, prettyPrint);
156    }
157
158    public static void writeOperation(JsonGenerator jg, OperationDocumentation op) throws IOException {
159        writeOperation(jg, op, false);
160    }
161
162    /**
163     * @since 5.9.4
164     */
165    public static void writeOperation(JsonGenerator jg, OperationDocumentation op, boolean prettyPrint)
166            throws IOException {
167        writeOperation(jg, op, op.url, prettyPrint);
168    }
169
170    public static void writeOperation(JsonGenerator jg, OperationDocumentation op, String url) throws IOException {
171        writeOperation(jg, op, url, false);
172    }
173
174    /**
175     * @since 5.9.4
176     */
177    public static void writeOperation(JsonGenerator jg, OperationDocumentation op, String url, boolean prettyPrint)
178            throws IOException {
179        if (prettyPrint) {
180            jg.useDefaultPrettyPrinter();
181        }
182        jg.writeStartObject();
183        jg.writeStringField("id", op.id);
184        if (op.getAliases() != null && op.getAliases().length > 0) {
185            jg.writeArrayFieldStart("aliases");
186            for (String alias : op.getAliases()) {
187                jg.writeString(alias);
188            }
189            jg.writeEndArray();
190        }
191        jg.writeStringField("label", op.label);
192        jg.writeStringField("category", op.category);
193        jg.writeStringField("requires", op.requires);
194        jg.writeStringField("description", op.description);
195        if (op.since != null && op.since.length() > 0) {
196            jg.writeStringField("since", op.since);
197        }
198        jg.writeStringField("url", url);
199        jg.writeArrayFieldStart("signature");
200        for (String s : op.signature) {
201            jg.writeString(s);
202        }
203        jg.writeEndArray();
204        writeParams(jg, Arrays.asList(op.params));
205        if (op.widgetDefinitions != null && op.widgetDefinitions.length > 0) {
206            jg.writeArrayFieldStart("widgets");
207            for (WidgetDefinition wdef : op.widgetDefinitions) {
208                jg.writeObject(JSONLayoutExporter.exportToJson(wdef, null, null));
209            }
210            jg.writeEndArray();
211        }
212        jg.writeEndObject();
213        jg.flush();
214    }
215
216    private static void writeParams(JsonGenerator jg, List<Param> params) throws IOException {
217        jg.writeArrayFieldStart("params");
218        for (Param p : params) {
219            jg.writeStartObject();
220            jg.writeStringField("name", p.name);
221            jg.writeStringField("description", p.description);
222            jg.writeStringField("type", p.type);
223            jg.writeBooleanField("required", p.isRequired);
224
225            jg.writeStringField("widget", p.widget);
226            jg.writeNumberField("order", p.order);
227            jg.writeArrayFieldStart("values");
228            for (String value : p.values) {
229                jg.writeString(value);
230            }
231            jg.writeEndArray();
232            jg.writeEndObject();
233        }
234        jg.writeEndArray();
235    }
236
237    public static void writeLogin(OutputStream out, LoginInfo login) throws IOException {
238        writeLogin(createGenerator(out), login);
239    }
240
241    public static void writeLogin(JsonGenerator jg, LoginInfo login) throws IOException {
242        jg.writeStartObject();
243        jg.writeStringField("entity-type", "login");
244        jg.writeStringField("username", login.getUsername());
245        jg.writeBooleanField("isAdministrator", login.isAdministrator());
246        jg.writeArrayFieldStart("groups");
247        for (String group : login.getGroups()) {
248            jg.writeString(group);
249        }
250        jg.writeEndArray();
251        jg.writeEndObject();
252        jg.flush();
253    }
254
255    public static void writePrimitive(OutputStream out, Object value) throws IOException {
256        writePrimitive(createGenerator(out), value);
257    }
258
259    public static void writePrimitive(JsonGenerator jg, Object value) throws IOException {
260        jg.writeStartObject();
261        jg.writeStringField("entity-type", "primitive");
262        if (value != null) {
263            Class<?> type = value.getClass();
264            if (type == String.class) {
265                jg.writeStringField("value", (String) value);
266            } else if (type == Boolean.class) {
267                jg.writeBooleanField("value", (Boolean) value);
268            } else if (type == Long.class) {
269                jg.writeNumberField("value", ((Number) value).longValue());
270            } else if (type == Double.class) {
271                jg.writeNumberField("value", ((Number) value).doubleValue());
272            } else if (type == Integer.class) {
273                jg.writeNumberField("value", ((Number) value).intValue());
274            } else if (type == Float.class) {
275                jg.writeNumberField("value", ((Number) value).floatValue());
276            }
277        } else {
278            jg.writeNullField("value");
279        }
280        jg.writeEndObject();
281        jg.flush();
282    }
283
284    /**
285     * @deprecated since 6.0 - use
286     *             {@link org.nuxeo.ecm.webengine.app .JsonWebengineWriter#writeException(java.io.OutputStream, org.nuxeo.ecm.webengine.WebException)}
287     *             instead
288     */
289    @Deprecated
290    public static void writeException(OutputStream out, WebException eh) throws IOException {
291        writeException(createGenerator(out), eh);
292    }
293
294    /**
295     * @deprecated since 6.0 - use
296     *             {@link org.nuxeo.ecm.webengine.app .JsonWebengineWriter#writeException(org.codehaus.jackson.JsonGenerator, org.nuxeo.ecm.webengine.WebException)}
297     *             instead
298     */
299    @Deprecated
300    public static void writeException(JsonGenerator jg, WebException eh) throws IOException {
301        jg.writeStartObject();
302        jg.writeStringField("entity-type", "exception");
303        jg.writeStringField("type", eh.getType());
304        jg.writeNumberField("status", eh.getStatus());
305        jg.writeStringField("message", eh.getMessage());
306        jg.writeStringField("stack", eh.getStackTraceString());
307        jg.writeObjectField("cause", eh.getCause());
308        jg.writeEndObject();
309        jg.flush();
310    }
311
312}