diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index f4bcb0fe6d2..47052f3e22b 100644 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -373,10 +373,10 @@ public class ApiServer implements HttpRequestHandler { if (objectId != null) { return ((BaseAsyncCreateCmd)asyncCmd).getResponse(jobId, objectId); } - return ApiResponseSerializer.toSerializedString(asyncCmd.getResponse(jobId)); + return ApiResponseSerializer.toSerializedString(asyncCmd.getResponse(jobId), asyncCmd.getResponseType()); } else { _dispatcher.dispatch(cmdObj, params); - return ApiResponseSerializer.toSerializedString(cmdObj.getResponse()); + return ApiResponseSerializer.toSerializedString(cmdObj.getResponse(), cmdObj.getResponseType()); } } diff --git a/server/src/com/cloud/api/BaseAsyncCreateCmd.java b/server/src/com/cloud/api/BaseAsyncCreateCmd.java index 94706522211..783e77022a3 100644 --- a/server/src/com/cloud/api/BaseAsyncCreateCmd.java +++ b/server/src/com/cloud/api/BaseAsyncCreateCmd.java @@ -20,6 +20,6 @@ public abstract class BaseAsyncCreateCmd extends BaseAsyncCmd { response.setJobId(jobId); response.setId(objectId); response.setResponseName(getName()); - return ApiResponseSerializer.toSerializedString(response); + return ApiResponseSerializer.toSerializedString(response, getResponseType()); } } diff --git a/server/src/com/cloud/api/BaseCmd.java b/server/src/com/cloud/api/BaseCmd.java index 5b7891cf38b..62572b2c095 100755 --- a/server/src/com/cloud/api/BaseCmd.java +++ b/server/src/com/cloud/api/BaseCmd.java @@ -27,7 +27,6 @@ import java.util.Map; import org.apache.log4j.Logger; -import com.cloud.server.ManagementServerImpl; import com.cloud.user.Account; import com.cloud.utils.Pair; @@ -94,6 +93,20 @@ public abstract class BaseCmd { private Object _responseObject = null; + @Parameter(name="response", type=CommandType.STRING) + private String responseType; + + public String getResponseType() { + if (responseType == null) { + return RESPONSE_TYPE_XML; + } + return responseType; + } + + public void setResponseType(String responseType) { + this.responseType = responseType; + } + public abstract String getName(); public abstract ResponseObject getResponse(); @@ -105,7 +118,6 @@ public abstract class BaseCmd { _responseObject = responseObject; } - public String getDateString(Date date) { if (date == null) { return ""; diff --git a/server/src/com/cloud/api/response/ApiResponseSerializer.java b/server/src/com/cloud/api/response/ApiResponseSerializer.java index c7a25625952..92cd4d213b8 100644 --- a/server/src/com/cloud/api/response/ApiResponseSerializer.java +++ b/server/src/com/cloud/api/response/ApiResponseSerializer.java @@ -1,15 +1,32 @@ package com.cloud.api.response; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; +import org.apache.log4j.Logger; + +import com.cloud.api.ApiDBUtils; +import com.cloud.api.BaseCmd; import com.cloud.api.ResponseObject; import com.cloud.serializer.GsonHelper; import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; public class ApiResponseSerializer { - // FIXME: what about XML response? - public static String toSerializedString(ResponseObject result) { + private static final Logger s_logger = Logger.getLogger(ApiResponseSerializer.class.getName()); + + public static String toSerializedString(ResponseObject result, String responseType) { + if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { + return toJSONSerializedString(result); + } else { + return toXMLSerializedString(result); + } + } + + private static String toJSONSerializedString(ResponseObject result) { if (result != null) { Gson gson = GsonHelper.getBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create(); StringBuilder sb = new StringBuilder(); @@ -30,7 +47,6 @@ public class ApiResponseSerializer { sb.append("{ }"); } } else { - // FIXME: nested objects? String jsonStr = gson.toJson(result); if ((jsonStr != null) && !"".equals(jsonStr)) { sb.append(jsonStr); @@ -43,4 +59,111 @@ public class ApiResponseSerializer { } return null; } + + private static String toXMLSerializedString(ResponseObject result) { + StringBuilder sb = new StringBuilder(); + sb.append(""); + sb.append("<" + result.getResponseName() + " cloud-stack-version=\""+ApiDBUtils.getVersion()+ "\">"); + + if (result instanceof ListResponse) { + List responses = ((ListResponse)result).getResponses(); + if ((responses != null) && !responses.isEmpty()) { + for (ResponseObject obj : responses) { + serializeResponseObjXML(sb, obj); + } + } + } else { + serializeResponseObjFieldsXML(sb, result); + } + + sb.append(""); + return sb.toString(); + } + + private static void serializeResponseObjXML(StringBuilder sb, ResponseObject obj) { + sb.append("<" + obj.getResponseName() + ">"); + serializeResponseObjFieldsXML(sb, obj); + sb.append(""); + } + + private static void serializeResponseObjFieldsXML(StringBuilder sb, ResponseObject obj) { + Field[] fields = obj.getClass().getDeclaredFields(); + for (Field field : fields) { + if ((field.getModifiers() & Modifier.TRANSIENT) != 0) { + continue; // skip transient fields + } + + SerializedName serializedName = field.getAnnotation(SerializedName.class); + if (serializedName == null) { + continue; // skip fields w/o serialized name + } + + String propName = field.getName(); + Method method = getGetMethod(obj, propName); + if (method != null) { + try { + Object fieldValue = method.invoke(obj); + if (fieldValue != null) { + if (fieldValue instanceof ResponseObject) { + ResponseObject subObj = (ResponseObject)fieldValue; + serializeResponseObjXML(sb, subObj); + } else { + sb.append("<" + serializedName.value() + ">" + fieldValue.toString() + ""); + } + } + } catch (IllegalArgumentException e) { + s_logger.error("Illegal argument exception when calling ResponseObject " + obj.getClass().getName() + " get method for property: " + propName); + } catch (IllegalAccessException e) { + s_logger.error("Illegal access exception when calling ResponseObject " + obj.getClass().getName() + " get method for property: " + propName); + } catch (InvocationTargetException e) { + s_logger.error("Invocation target exception when calling ResponseObject " + obj.getClass().getName() + " get method for property: " + propName); + } + } + } + } + + private static Method getGetMethod(Object o, String propName) { + Method method = null; + String methodName = getGetMethodName("get", propName); + try { + method = o.getClass().getMethod(methodName); + } catch (SecurityException e1) { + s_logger.error("Security exception in getting ResponseObject " + o.getClass().getName() + " get method for property: " + + propName); + } catch (NoSuchMethodException e1) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("ResponseObject " + o.getClass().getName() + " does not have " + methodName + + "() method for property: " + propName + + ", will check is-prefixed method to see if it is boolean property"); + } + } + + if (method != null) + return method; + + methodName = getGetMethodName("is", propName); + try { + method = o.getClass().getMethod(methodName); + } catch (SecurityException e1) { + s_logger.error("Security exception in getting ResponseObject " + o.getClass().getName() + " get method for property: " + + propName); + } catch (NoSuchMethodException e1) { + s_logger.warn("ResponseObject " + o.getClass().getName() + " does not have " + methodName + "() method for property: " + + propName); + } + return method; + } + + private static String getGetMethodName(String prefix, String fieldName) { + StringBuffer sb = new StringBuffer(prefix); + + if (fieldName.length() >= prefix.length() && fieldName.substring(0, prefix.length()).equals(prefix)) { + return fieldName; + } else { + sb.append(fieldName.substring(0, 1).toUpperCase()); + sb.append(fieldName.substring(1)); + } + + return sb.toString(); + } }