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