diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationProcessor.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationProcessor.java new file mode 100644 index 0000000000..730c8c82dc --- /dev/null +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationProcessor.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.apimgt.webapp.publisher.lifecycle.util; + +import org.apache.catalina.core.StandardContext; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.scannotation.AnnotationDB; +import org.scannotation.WarUrlFinder; +import org.wso2.carbon.apimgt.annotations.api.API; +import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.apimgt.api.model.Scope; +import org.wso2.carbon.apimgt.webapp.publisher.config.APIResource; +import org.wso2.carbon.apimgt.webapp.publisher.config.APIResourceConfiguration; +import org.wso2.carbon.apimgt.webapp.publisher.config.PermissionConfiguration; +import org.wso2.carbon.apimgt.webapp.publisher.config.PermissionManagementException; +import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; +import org.wso2.carbon.device.mgt.core.config.deviceType.DTConfiguration; + +import javax.servlet.ServletContext; +import javax.ws.rs.*; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class AnnotationProcessor { + + private static final Log log = LogFactory.getLog(AnnotationProcessor.class); + + private static final String PACKAGE_ORG_APACHE = "org.apache"; + private static final String PACKAGE_ORG_CODEHAUS = "org.codehaus"; + private static final String PACKAGE_ORG_SPRINGFRAMEWORK = "org.springframework"; + + private static final String AUTH_TYPE = "Any"; + private static final String PROTOCOL_HTTP = "http"; + private static final String SERVER_HOST = "carbon.local.ip"; + private static final String HTTP_PORT = "httpPort"; + private static final String STRING_ARR = "string_arr"; + private static final String STRING = "string"; + + private StandardContext context; + private Method[] pathClazzMethods; + private Class pathClazz; + Class apiClazz; + private ClassLoader classLoader; + private ServletContext servletContext; + + + public AnnotationProcessor(final StandardContext context) { + this.context = context; + servletContext = context.getServletContext(); + classLoader = servletContext.getClassLoader(); + } + + /** + * Scan the context for classes with annotations + * + * @return + * @throws IOException + */ + public Set scanStandardContext(String className) throws IOException { + AnnotationDB db = new AnnotationDB(); + db.addIgnoredPackages(PACKAGE_ORG_APACHE); + db.addIgnoredPackages(PACKAGE_ORG_CODEHAUS); + db.addIgnoredPackages(PACKAGE_ORG_SPRINGFRAMEWORK); + + URL[] libPath = WarUrlFinder.findWebInfLibClasspaths(servletContext); + URL classPath = WarUrlFinder.findWebInfClassesPath(servletContext); + URL[] urls = (URL[]) ArrayUtils.add(libPath, libPath.length, classPath); + + db.scanArchives(urls); + + //Returns a list of classes with given Annotation + return db.getAnnotationIndex().get(className); + } + + /** + * Method identifies the URL templates and context by reading the annotations of a class + * + * @param entityClasses + * @return + */ + public List extractAPIInfo(final ServletContext servletContext, Set entityClasses) + throws ClassNotFoundException { + + List apiResourceConfigs = new ArrayList(); + + if (entityClasses != null && !entityClasses.isEmpty()) { + for (final String className : entityClasses) { + + APIResourceConfiguration resource = + AccessController.doPrivileged(new PrivilegedAction() { + public APIResourceConfiguration run() { + Class clazz = null; + APIResourceConfiguration apiResourceConfig = null; + try { + clazz = classLoader.loadClass(className); + + apiClazz = (Class) + classLoader.loadClass(org.wso2.carbon.apimgt.annotations.api.API + .class.getName()); + + Annotation apiAnno = clazz.getAnnotation(apiClazz); + List resourceList; + + if (apiAnno != null) { + + if (log.isDebugEnabled()) { + log.debug("Application Context root = " + servletContext.getContextPath()); + } + + try { + apiResourceConfig = processAPIAnnotation(apiAnno); + // All the apis should map to same root "/" + String rootContext = servletContext.getContextPath(); + pathClazz = (Class) classLoader.loadClass(Path.class.getName()); + pathClazzMethods = pathClazz.getMethods(); + + Annotation rootContectAnno = clazz.getAnnotation(pathClazz); + String subContext = ""; + if (rootContectAnno != null) { + subContext = invokeMethod(pathClazzMethods[0], rootContectAnno, STRING); + if (subContext != null && !subContext.isEmpty()) { + rootContext = rootContext + "/" + subContext; + } else { + subContext = ""; + } + if (log.isDebugEnabled()) { + log.debug("API Root Context = " + rootContext); + } + } + + Method[] annotatedMethods = clazz.getDeclaredMethods(); + resourceList = getApiResources(rootContext, subContext, annotatedMethods); + apiResourceConfig.setResources(resourceList); + } catch (Throwable throwable) { + log.error("Error encountered while scanning for annotations", throwable); + } + } + } catch (ClassNotFoundException e) { + log.error("Error when passing the api annotation for device type apis."); + } + return apiResourceConfig; + } + }); + apiResourceConfigs.add(resource); + } + } + return apiResourceConfigs; + } + + /** + * Iterate API annotation and build API Configuration + * + * @param apiAnno + * @return + * @throws Throwable + */ + private APIResourceConfiguration processAPIAnnotation(Annotation apiAnno) throws Throwable { + Method[] apiClazzMethods = apiClazz.getMethods(); + APIResourceConfiguration apiResourceConfig = new APIResourceConfiguration(); + for (int k = 0; k < apiClazzMethods.length; k++) { + switch (apiClazzMethods[k].getName()) { + case "name": + apiResourceConfig.setName(invokeMethod(apiClazzMethods[k], apiAnno, STRING)); + break; + case "version": + apiResourceConfig.setVersion(invokeMethod(apiClazzMethods[k], apiAnno, STRING)); + break; + case "context": + apiResourceConfig.setContext(invokeMethod(apiClazzMethods[k], apiAnno, STRING)); + break; + case "tags": + apiResourceConfig.setTags(invokeMethod(apiClazzMethods[k], apiAnno)); + break; + } + } + return apiResourceConfig; + } + + + /** + * Get Resources for each API + * + * @param resourceRootContext + * @param apiRootContext + * @param annotatedMethods + * @return + * @throws Throwable + */ + private List getApiResources(String resourceRootContext, String apiRootContext, + Method[] annotatedMethods) throws Throwable { + List resourceList; + resourceList = new ArrayList(); + for (Method method : annotatedMethods) { + Annotation methodContextAnno = method.getAnnotation(pathClazz); + if (methodContextAnno != null) { + String subCtx = invokeMethod(pathClazzMethods[0], methodContextAnno, STRING); + APIResource resource = new APIResource(); + resource.setUriTemplate(makeContextURLReady(apiRootContext + subCtx)); + + DTConfiguration deviceTypeConfig = DeviceConfigurationManager.getInstance(). + getDeviceManagementConfig().getDTDeploymentConfiguration(); + + String serverIP = deviceTypeConfig.getDtHostAddress(); + String httpServerPort = deviceTypeConfig.getDtHostPort(); + + resource.setUri(PROTOCOL_HTTP + "://" + serverIP + ":" + httpServerPort + makeContextURLReady( + resourceRootContext) + makeContextURLReady(subCtx)); + resource.setAuthType(AUTH_TYPE); + + Annotation[] annotations = method.getDeclaredAnnotations(); + for (int i = 0; i < annotations.length; i++) { + processHTTPMethodAnnotation(resource, annotations[i]); + if (annotations[i].annotationType().getName().equals(Consumes.class.getName())) { + Class consumesClass = (Class) classLoader.loadClass( + Consumes.class.getName()); + Method[] consumesClassMethods = consumesClass.getMethods(); + Annotation consumesAnno = method.getAnnotation(consumesClass); + resource.setConsumes(invokeMethod(consumesClassMethods[0], consumesAnno, STRING_ARR)); + } + if (annotations[i].annotationType().getName().equals(Produces.class.getName())) { + Class producesClass = (Class) classLoader.loadClass( + Produces.class.getName()); + Method[] producesClassMethods = producesClass.getMethods(); + Annotation producesAnno = method.getAnnotation(producesClass); + resource.setProduces(invokeMethod(producesClassMethods[0], producesAnno, STRING_ARR)); + } + if (annotations[i].annotationType().getName().equals(Permission.class.getName())) { + PermissionConfiguration permissionConf = this.getPermission(method); + if (permissionConf != null) { + Scope scope = new Scope(); + scope.setKey(permissionConf.getScopeName()); + scope.setDescription(permissionConf.getScopeName()); + scope.setName(permissionConf.getScopeName()); + String roles = StringUtils.join(permissionConf.getPermissions(), ","); + scope.setRoles(roles); + resource.setScope(scope); + } + } + } + resourceList.add(resource); + } + } + return resourceList; + } + + /** + * Read Method annotations indicating HTTP Methods + * + * @param resource + * @param annotation + */ + private void processHTTPMethodAnnotation(APIResource resource, Annotation annotation) { + if (annotation.annotationType().getName().equals(GET.class.getName())) { + resource.setHttpVerb(HttpMethod.GET); + } + if (annotation.annotationType().getName().equals(POST.class.getName())) { + resource.setHttpVerb(HttpMethod.POST); + } + if (annotation.annotationType().getName().equals(OPTIONS.class.getName())) { + resource.setHttpVerb(HttpMethod.OPTIONS); + } + if (annotation.annotationType().getName().equals(DELETE.class.getName())) { + resource.setHttpVerb(HttpMethod.DELETE); + } + if (annotation.annotationType().getName().equals(PUT.class.getName())) { + resource.setHttpVerb(HttpMethod.PUT); + } + } + + /** + * Append '/' to the context and make it URL ready + * + * @param context + * @return + */ + private String makeContextURLReady(String context) { + if (context != null && !context.equalsIgnoreCase("")) { + if (context.startsWith("/")) { + return context; + } else { + return "/" + context; + } + } + return ""; + } + + /** + * When an annotation and method is passed, this method invokes that executes said method against the annotation + * + * @param method + * @param annotation + * @param returnType + * @return + * @throws Throwable + */ + private String invokeMethod(Method method, Annotation annotation, String returnType) throws Throwable { + InvocationHandler methodHandler = Proxy.getInvocationHandler(annotation); + switch (returnType) { + case STRING: + return (String) methodHandler.invoke(annotation, method, null); + case STRING_ARR: + return ((String[]) methodHandler.invoke(annotation, method, null))[0]; + default: + return null; + } + } + + /** + * When an annotation and method is passed, this method invokes that executes said method against the annotation + */ + private String[] invokeMethod(Method method, Annotation annotation) throws Throwable { + InvocationHandler methodHandler = Proxy.getInvocationHandler(annotation); + return ((String[]) methodHandler.invoke(annotation, method, null)); + } + + private PermissionConfiguration getPermission(Method currentMethod) throws Throwable { + Class permissionClass = (Class) classLoader.loadClass(Permission.class.getName()); + Annotation permissionAnnotation = currentMethod.getAnnotation(permissionClass); + if (permissionClass != null) { + Method[] permissionClassMethods = permissionClass.getMethods(); + PermissionConfiguration permissionConf = new PermissionConfiguration(); + for (Method method : permissionClassMethods) { + switch (method.getName()) { + case "scope": + permissionConf.setScopeName(invokeMethod(method, permissionAnnotation, STRING)); + break; + case "permissions": + String permissions[] = invokeMethod(method, permissionAnnotation); + this.addPermission(permissions); + permissionConf.setPermissions(permissions); + break; + } + } + return permissionConf; + } + return null; + } + + private void addPermission(String[] permissions) throws PermissionManagementException { + for (String permission : permissions) { + PermissionUtils.addPermission(permission); + } + } + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/deviceType/DTConfiguration.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/deviceType/DTConfiguration.java new file mode 100644 index 0000000000..14a3af1274 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/deviceType/DTConfiguration.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +/** + * This class will read the configurations related to task. This task will be responsible for adding the operations. + */ +package org.wso2.carbon.device.mgt.core.config.deviceType; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "DTDepyloymentConfiguration") +public class DTConfiguration { + + private String dtHostAddress; + private String dtHostPort; + + @XmlElement(name = "DTHostAddress", required = true) + public String getDtHostAddress() { + return dtHostAddress; + } + + public void setDtHostAddress(String dtHostAddress) { + this.dtHostAddress = dtHostAddress; + } + + @XmlElement(name = "DTHostPort", required = true) + public String getDtHostPort() { + return dtHostPort; + } + + public void setDtHostPort(String dtHostPort) { + this.dtHostPort = dtHostPort; + } + + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/main/java/org/wso2/carbon/device/mgt/extensions/feature/mgt/util/AnnotationProcessor.java b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/main/java/org/wso2/carbon/device/mgt/extensions/feature/mgt/util/AnnotationProcessor.java new file mode 100644 index 0000000000..2819d47ee1 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/main/java/org/wso2/carbon/device/mgt/extensions/feature/mgt/util/AnnotationProcessor.java @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.device.mgt.extensions.feature.mgt.util; + +import org.apache.catalina.core.StandardContext; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.scannotation.AnnotationDB; +import org.scannotation.WarUrlFinder; +import org.wso2.carbon.device.mgt.common.Feature; +import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.DeviceType; + +import javax.servlet.ServletContext; +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * This has the utility function to extract feature information. + */ +public class AnnotationProcessor { + + private static final Log log = LogFactory.getLog(AnnotationProcessor.class); + + private static final String PACKAGE_ORG_APACHE = "org.apache"; + private static final String PACKAGE_ORG_CODEHAUS = "org.codehaus"; + private static final String PACKAGE_ORG_SPRINGFRAMEWORK = "org.springframework"; + private static final String STRING_ARR = "string_arr"; + private static final String STRING = "string"; + private static final String METHOD = "method"; + private Class + featureAnnotationClazz; + private ClassLoader classLoader; + private ServletContext servletContext; + + + public AnnotationProcessor(final StandardContext context) { + servletContext = context.getServletContext(); + classLoader = servletContext.getClassLoader(); + } + + /** + * Scan the context for classes with annotations + */ + public Set scanStandardContext(String className) throws IOException { + AnnotationDB db = new AnnotationDB(); + db.addIgnoredPackages(PACKAGE_ORG_APACHE); + db.addIgnoredPackages(PACKAGE_ORG_CODEHAUS); + db.addIgnoredPackages(PACKAGE_ORG_SPRINGFRAMEWORK); + URL[] libPath = WarUrlFinder.findWebInfLibClasspaths(servletContext); + URL classPath = WarUrlFinder.findWebInfClassesPath(servletContext); + URL[] urls = (URL[]) ArrayUtils.add(libPath, libPath.length, classPath); + db.scanArchives(urls); + + //Returns a list of classes with given Annotation + return db.getAnnotationIndex().get(className); + } + + /** + * Method identifies the URL templates and context by reading the annotations of a class + */ + public Map> extractFeatures(Set entityClasses) throws ClassNotFoundException { + Map> features = null; + if (entityClasses != null && !entityClasses.isEmpty()) { + features = new HashMap<>(); + for (final String className : entityClasses) { + final Map> featureMap = + AccessController.doPrivileged(new PrivilegedAction>>() { + public Map> run() { + Map> featureMap = new HashMap<>(); + try { + Class clazz = classLoader.loadClass(className); + Class deviceTypeClazz = (Class) classLoader.loadClass( + DeviceType.class.getName()); + Annotation deviceTypeAnno = clazz.getAnnotation(deviceTypeClazz); + if (deviceTypeAnno != null) { + Method[] deviceTypeMethod = deviceTypeClazz.getMethods(); + String deviceType = invokeMethod(deviceTypeMethod[0], deviceTypeAnno, STRING); + featureAnnotationClazz = + (Class) classLoader.loadClass( + org.wso2.carbon.device.mgt.extensions.feature.mgt + .annotations.Feature.class.getName()); + List featureList = getFeatures(clazz.getDeclaredMethods()); + featureMap.put(deviceType, featureList); + } + } catch (Throwable e) { + log.error("Failed to load the annotation from the features in the " + + "class " + className, e); + } + return featureMap; + } + }); + + features.putAll(featureMap); + } + } + return features; + } + + private List getFeatures(Method[] methodsList) throws Throwable { + List featureList = new ArrayList<>(); + for (Method currentMethod : methodsList) { + Annotation featureAnnotation = currentMethod.getAnnotation(featureAnnotationClazz); + if (featureAnnotation != null) { + Feature feature = new Feature(); + feature = processFeatureAnnotation(feature, currentMethod); + Annotation[] annotations = currentMethod.getDeclaredAnnotations(); + Feature.MetadataEntry metadataEntry = new Feature.MetadataEntry(); + metadataEntry.setId(-1); + Map apiParams = new HashMap<>(); + for (int i = 0; i < annotations.length; i++) { + Annotation currentAnnotation = annotations[i]; + processHttpMethodAnnotation(apiParams, currentAnnotation); + if (currentAnnotation.annotationType().getName().equals(Path.class.getName())) { + String uri = getPathAnnotationValue(currentMethod); + apiParams.put("uri", uri); + } + apiParams = processParamAnnotations(apiParams, currentMethod); + } + metadataEntry.setValue(apiParams); + List metaInfoList = new ArrayList<>(); + metaInfoList.add(metadataEntry); + feature.setMetadataEntries(metaInfoList); + featureList.add(feature); + } + } + return featureList; + } + + private Map processParamAnnotations(Map apiParams, Method currentMethod) + throws Throwable{ + try { + apiParams.put("pathParams", processParamAnnotations(currentMethod, PathParam.class)); + apiParams.put("queryParams", processParamAnnotations(currentMethod, QueryParam.class)); + apiParams.put("formParams", processParamAnnotations(currentMethod, FormParam.class)); + } catch (ClassNotFoundException e) { + log.debug("No Form Param found for class " + featureAnnotationClazz.getName()); + } + return apiParams; + } + + private List processParamAnnotations(Method currentMethod, Class clazz) throws Throwable{ + List params = new ArrayList<>(); + try { + Class paramClazz = (Class) classLoader.loadClass(clazz.getName()); + Method[] formMethods = paramClazz.getMethods(); + //Extract method parameter information and store same as feature meta info + Annotation[][] paramAnnotations = currentMethod.getParameterAnnotations(); + Method valueMethod = formMethods[0]; + for (int j = 0; j < paramAnnotations.length; j++) { + for (Annotation anno : paramAnnotations[j]) { + if (anno.annotationType().getName().equals(clazz.getName())) { + params.add(invokeMethod(valueMethod, anno, STRING)); + } + } + } + } catch (ClassNotFoundException e) { + log.debug("No "+ clazz.getName() +" Param found for class " + featureAnnotationClazz.getName()); + } + return params; + } + + /** + * Read Method annotations indicating HTTP Methods + */ + private void processHttpMethodAnnotation(Map apiParams, Annotation currentAnnotation) { + //Extracting method with which feature is exposed + if (currentAnnotation.annotationType().getName().equals(GET.class.getName())) { + apiParams.put(METHOD, HttpMethod.GET); + } else if (currentAnnotation.annotationType().getName().equals(POST.class.getName())) { + apiParams.put(METHOD, HttpMethod.POST); + } else if (currentAnnotation.annotationType().getName().equals(OPTIONS.class.getName())) { + apiParams.put(METHOD, HttpMethod.OPTIONS); + } else if (currentAnnotation.annotationType().getName().equals(DELETE.class.getName())) { + apiParams.put(METHOD, HttpMethod.DELETE); + } else if (currentAnnotation.annotationType().getName().equals(PUT.class.getName())) { + apiParams.put(METHOD, HttpMethod.PUT); + } + } + + /** + * Read Feature annotation and Identify Features + * @param feature + * @param currentMethod + * @return + * @throws Throwable + */ + private Feature processFeatureAnnotation(Feature feature, Method currentMethod) throws Throwable{ + Method[] featureAnnoMethods = featureAnnotationClazz.getMethods(); + Annotation featureAnno = currentMethod.getAnnotation(featureAnnotationClazz); + for (int k = 0; k < featureAnnoMethods.length; k++) { + switch (featureAnnoMethods[k].getName()) { + case "name": + feature.setName(invokeMethod(featureAnnoMethods[k], featureAnno, STRING)); + break; + case "code": + feature.setCode(invokeMethod(featureAnnoMethods[k], featureAnno, STRING)); + break; + case "description": + feature.setDescription(invokeMethod(featureAnnoMethods[k], featureAnno, STRING)); + break; + } + } + return feature; + } + + /** + * Get value depicted by Path Annotation + * @param currentMethod + * @return + * @throws Throwable + */ + public String getPathAnnotationValue(Method currentMethod) throws Throwable{ + String uri = ""; + try { + Class pathClazz = (Class) classLoader.loadClass(Path.class.getName()); + Annotation pathAnnno = currentMethod.getAnnotation(pathClazz); + Method[] pathMethods = pathClazz.getMethods(); + Method valueMethod = pathMethods[0]; + uri = invokeMethod(valueMethod, pathAnnno, STRING); + } catch (ClassNotFoundException e) { + log.debug("No Path Param found for class " + featureAnnotationClazz.getName()); + } + return uri; + } + + /** + * When an annotation and method is passed, this method invokes that executes said method against the annotation + */ + private String invokeMethod(Method method, Annotation annotation, String returnType) throws Throwable { + InvocationHandler methodHandler = Proxy.getInvocationHandler(annotation); + switch (returnType) { + case STRING: + return (String) methodHandler.invoke(annotation, method, null); + case STRING_ARR: + return ((String[]) methodHandler.invoke(annotation, method, null))[0]; + default: + return null; + } + } +} \ No newline at end of file