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