From 86a77e29dc26fee1bb2169a9a1424bdd4e52f65e Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 22:52:10 -0800 Subject: [PATCH] ApiDiscovery: Fix listApis to return api response, related apis etc. - Fix method to return listApis per api name basis - Return api response, api related cmd etc. as part of response - Caching and processing all cmd, response classes when plugin starts, made class list, maps static so they are shared by multiple instances in case, takes about 1306ms to do the processsing but only on load time - Cache for first listApi() and return precached data thereon, takes 2.2ms for first call, during runtime and 0ms thereon Signed-off-by: Rohit Yadav --- .../apache/cloudstack/api/ApiConstants.java | 1 + .../command/user/discovery/ListApisCmd.java | 22 ++-- .../discovery/ApiDiscoveryService.java | 2 +- .../discovery/ApiDiscoveryServiceImpl.java | 102 +++++++++++++++--- 4 files changed, 103 insertions(+), 24 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index b4ce24c2bc9..d3bfcd66afc 100644 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -158,6 +158,7 @@ public class ApiConstants { public static final String RECEIVED_BYTES = "receivedbytes"; public static final String REQUIRES_HVM = "requireshvm"; public static final String RESOURCE_TYPE = "resourcetype"; + public static final String RESPONSE = "response"; public static final String QUERY_FILTER = "queryfilter"; public static final String SCHEDULE = "schedule"; public static final String SCOPE = "scope"; diff --git a/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java b/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java index feab20ac5cf..ed3e1751027 100644 --- a/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java +++ b/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java @@ -16,12 +16,12 @@ // under the License. package org.apache.cloudstack.api.command.user.discovery; -import com.cloud.user.Account; import com.cloud.user.UserContext; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.PlugService; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ListResponse; @@ -30,8 +30,8 @@ import org.apache.cloudstack.api.response.ApiDiscoveryResponse; import org.apache.log4j.Logger; -@APICommand(name = "listApis", responseObject = ApiDiscoveryResponse.class, description = "lists all available apis on the server, provided by Api Discovery plugin", since = "4.1.0") -public class ListApisCmd extends BaseListCmd { +@APICommand(name = "listApis", responseObject = ApiDiscoveryResponse.class, description = "lists all available apis on the server, provided by the Api Discovery plugin", since = "4.1.0") +public class ListApisCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(ListApisCmd.class.getName()); private static final String s_name = "listapisresponse"; @@ -39,14 +39,16 @@ public class ListApisCmd extends BaseListCmd { @PlugService ApiDiscoveryService _apiDiscoveryService; + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description="API name") + private String name; + @Override public void execute() throws ServerApiException { if (_apiDiscoveryService != null) { - Account caller = UserContext.current().getCaller(); RoleType roleType = _accountService.getRoleType(UserContext.current().getCaller()); - ListResponse response = (ListResponse) _apiDiscoveryService.listApis(roleType); + ListResponse response = (ListResponse) _apiDiscoveryService.listApis(roleType, name); if (response == null) { - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Api Discovery plugin was unable to find and process any apis"); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Api Discovery plugin was unable to find an api by that name or process any apis"); } response.setResponseName(getCommandName()); this.setResponseObject(response); @@ -57,4 +59,10 @@ public class ListApisCmd extends BaseListCmd { public String getCommandName() { return s_name; } + + @Override + public long getEntityOwnerId() { + // no owner is needed for list command + return 0; + } } diff --git a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java index a1d440e9ccf..611493bfc08 100644 --- a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java +++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java @@ -22,5 +22,5 @@ import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.response.ListResponse; public interface ApiDiscoveryService extends PluggableService { - ListResponse listApis(RoleType roleType); + ListResponse listApis(RoleType roleType, String apiName); } diff --git a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java index 9f4031c4769..5f84486ae49 100644 --- a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java +++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java @@ -16,13 +16,13 @@ // under the License. package org.apache.cloudstack.discovery; +import com.cloud.serializer.Param; import com.cloud.server.ManagementServer; import com.cloud.utils.ReflectUtil; -import com.cloud.utils.component.Adapters; +import com.cloud.utils.StringUtils; import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.component.Inject; import com.cloud.utils.component.PluggableService; -import org.apache.cloudstack.acl.APIChecker; +import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; @@ -32,6 +32,7 @@ import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ApiDiscoveryResponse; import org.apache.cloudstack.api.response.ApiParameterResponse; +import org.apache.cloudstack.api.response.ApiResponseResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.log4j.Logger; @@ -39,6 +40,7 @@ import javax.ejb.Local; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -47,19 +49,24 @@ import java.util.Set; public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { private static final Logger s_logger = Logger.getLogger(ApiDiscoveryServiceImpl.class); + private static Map> _roleTypeDiscoveryResponseListMap; + private static Map _apiNameDiscoveryResponseMap = new HashMap(); - private static Map> _roleTypeDiscoveryResponseListMap = - new HashMap>(); - private static Map> _apiNameRoleTypeListMap = null; protected ApiDiscoveryServiceImpl() { super(); - for (RoleType roleType: RoleType.values()) - _roleTypeDiscoveryResponseListMap.put(roleType, new ArrayList()); - cacheListApiResponse(); + if (_roleTypeDiscoveryResponseListMap == null) { + long startTime = System.nanoTime(); + _roleTypeDiscoveryResponseListMap = new HashMap>(); + for (RoleType roleType: RoleType.values()) + _roleTypeDiscoveryResponseListMap.put(roleType, new ArrayList()); + cacheResponseMap(); + long endTime = System.nanoTime(); + s_logger.info("Api Discovery Service: Annotation, docstrings, api relation graph processed in " + (endTime - startTime) / 1000000.0 + " ms"); + } } private Map> getApiNameRoleTypeListMap() { @@ -86,10 +93,12 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { return apiNameRoleTypeMap; } - private void cacheListApiResponse() { + private void cacheResponseMap() { Set> cmdClasses = ReflectUtil.getClassesWithAnnotation(APICommand.class, new String[]{"org.apache.cloudstack.api", "com.cloud.api"}); + Map> responseApiNameListMap = new HashMap>(); + for(Class cmdClass: cmdClasses) { APICommand apiCmdAnnotation = cmdClass.getAnnotation(APICommand.class); if (apiCmdAnnotation == null) @@ -100,10 +109,32 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { continue; String apiName = apiCmdAnnotation.name(); + String responseName = apiCmdAnnotation.responseObject().getName(); + if (!responseName.contains("SuccessResponse")) { + if (!responseApiNameListMap.containsKey(responseName)) + responseApiNameListMap.put(responseName, new ArrayList()); + responseApiNameListMap.get(responseName).add(apiName); + } ApiDiscoveryResponse response = new ApiDiscoveryResponse(); response.setName(apiName); response.setDescription(apiCmdAnnotation.description()); - response.setSince(apiCmdAnnotation.since()); + if (!apiCmdAnnotation.since().isEmpty()) + response.setSince(apiCmdAnnotation.since()); + response.setRelated(responseName); + + Field[] responseFields = apiCmdAnnotation.responseObject().getDeclaredFields(); + for(Field responseField: responseFields) { + SerializedName serializedName = responseField.getAnnotation(SerializedName.class); + if(serializedName != null) { + ApiResponseResponse responseResponse = new ApiResponseResponse(); + responseResponse.setName(serializedName.value()); + Param param = responseField.getAnnotation(Param.class); + if (param != null) + responseResponse.setDescription(param.description()); + responseResponse.setType(responseField.getType().getSimpleName().toLowerCase()); + response.addApiResponse(responseResponse); + } + } Field[] fields = ReflectUtil.getAllFieldsForClass(cmdClass, new Class[] {BaseCmd.class, BaseAsyncCmd.class, BaseAsyncCreateCmd.class}); @@ -122,23 +153,50 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { ApiParameterResponse paramResponse = new ApiParameterResponse(); paramResponse.setName(parameterAnnotation.name()); paramResponse.setDescription(parameterAnnotation.description()); - paramResponse.setType(parameterAnnotation.type().toString()); + paramResponse.setType(parameterAnnotation.type().toString().toLowerCase()); paramResponse.setLength(parameterAnnotation.length()); paramResponse.setRequired(parameterAnnotation.required()); - paramResponse.setSince(parameterAnnotation.since()); + if (!parameterAnnotation.since().isEmpty()) + paramResponse.setSince(parameterAnnotation.since()); + paramResponse.setRelated(parameterAnnotation.entityType()[0].getName()); response.addParam(paramResponse); } } - response.setObjectName("apis"); + response.setObjectName("api"); + _apiNameDiscoveryResponseMap.put(apiName, response); + } + + for (String apiName: _apiNameDiscoveryResponseMap.keySet()) { + ApiDiscoveryResponse response = _apiNameDiscoveryResponseMap.get(apiName); + Set processedParams = new HashSet(); + for (ApiParameterResponse param: response.getParams()) { + if (responseApiNameListMap.containsKey(param.getRelated())) { + List relatedApis = responseApiNameListMap.get(param.getRelated()); + param.setRelated(StringUtils.join(relatedApis, ",")); + } else { + param.setRelated(null); + } + processedParams.add(param); + } + response.setParams(processedParams); + + if (responseApiNameListMap.containsKey(response.getRelated())) { + List relatedApis = responseApiNameListMap.get(response.getRelated()); + relatedApis.remove(apiName); + response.setRelated(StringUtils.join(relatedApis, ",")); + } else { + response.setRelated(null); + } _apiNameDiscoveryResponseMap.put(apiName, response); } } @Override - public ListResponse listApis(RoleType roleType) { + public ListResponse listApis(RoleType roleType, String name) { // Creates roles based response list cache the first time listApis is called // Due to how adapters work, this cannot be done when mgmt loads if (_apiNameRoleTypeListMap == null) { + long startTime = System.nanoTime(); _apiNameRoleTypeListMap = getApiNameRoleTypeListMap(); for (Map.Entry> entry: _apiNameRoleTypeListMap.entrySet()) { String apiName = entry.getKey(); @@ -147,9 +205,21 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { _apiNameDiscoveryResponseMap.get(apiName)); } } + long endTime = System.nanoTime(); + s_logger.info("Api Discovery Service: List apis cached in " + (endTime - startTime) / 1000000.0 + " ms"); } ListResponse response = new ListResponse(); - response.setResponses(_roleTypeDiscoveryResponseListMap.get(roleType)); + if (name != null) { + if (!_apiNameDiscoveryResponseMap.containsKey(name)) + return null; + + List singleResponse = new ArrayList(); + singleResponse.add(_apiNameDiscoveryResponseMap.get(name)); + response.setResponses(singleResponse); + + } else { + response.setResponses(_roleTypeDiscoveryResponseListMap.get(roleType)); + } return response; }