| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF 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.apache.dubbo.metadata.extension.rest.annotation.processing.springmvc; |
| |
| import org.apache.dubbo.metadata.extension.rest.annotation.processing.AbstractServiceRestMetadataResolver; |
| import org.apache.dubbo.metadata.extension.rest.annotation.processing.ServiceRestMetadataResolver; |
| |
| import javax.annotation.processing.ProcessingEnvironment; |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.type.DeclaredType; |
| import java.lang.reflect.Array; |
| import java.util.Set; |
| |
| |
| import static java.lang.String.valueOf; |
| import static java.lang.reflect.Array.getLength; |
| import static java.util.stream.Stream.of; |
| import static org.apache.dubbo.common.function.Streams.filterFirst; |
| import static org.apache.dubbo.common.utils.ArrayUtils.isEmpty; |
| import static org.apache.dubbo.common.utils.ArrayUtils.isNotEmpty; |
| import static org.apache.dubbo.common.utils.PathUtils.buildPath; |
| import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.findAnnotation; |
| import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.findMetaAnnotation; |
| import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAllAnnotations; |
| import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAttribute; |
| import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.isAnnotationPresent; |
| import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SPRING_MVC.CONTROLLER_ANNOTATION_CLASS_NAME; |
| import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SPRING_MVC.REQUEST_MAPPING_ANNOTATION_CLASS_NAME; |
| |
| /** |
| * {@link ServiceRestMetadataResolver} |
| * |
| * @since 2.7.6 |
| */ |
| public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver { |
| |
| private static final int FIRST_ELEMENT_INDEX = 0; |
| |
| @Override |
| public boolean supports(ProcessingEnvironment processingEnvironment, TypeElement serviceType) { |
| return supports(serviceType); |
| } |
| |
| @Override |
| protected boolean supports( |
| ProcessingEnvironment processingEnv, |
| TypeElement serviceType, |
| TypeElement serviceInterfaceType, |
| ExecutableElement method) { |
| return isAnnotationPresent(method, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); |
| } |
| |
| public static boolean supports(TypeElement serviceType) { |
| // class @Controller or @RequestMapping |
| return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS_NAME) |
| || isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); |
| } |
| |
| @Override |
| protected String resolveRequestPath( |
| ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method) { |
| |
| String requestPathFromType = getRequestPath(serviceType); |
| |
| String requestPathFromMethod = getRequestPath(method); |
| |
| return buildPath(requestPathFromType, requestPathFromMethod); |
| } |
| |
| @Override |
| protected String resolveRequestMethod( |
| ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method) { |
| |
| AnnotationMirror requestMapping = getRequestMapping(method); |
| |
| // httpMethod is an array of RequestMethod |
| Object httpMethod = getAttribute(requestMapping, "method"); |
| |
| if (httpMethod == null || getLength(httpMethod) < 1) { |
| return null; |
| } |
| |
| // TODO Is is required to support more request methods? |
| return valueOf(Array.get(httpMethod, FIRST_ELEMENT_INDEX)); |
| } |
| |
| private AnnotationMirror getRequestMapping(Element element) { |
| // try "@RequestMapping" first |
| AnnotationMirror requestMapping = findAnnotation(element, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); |
| // try the annotation meta-annotated later |
| if (requestMapping == null) { |
| requestMapping = findMetaAnnotation(element, REQUEST_MAPPING_ANNOTATION_CLASS_NAME); |
| } |
| return requestMapping; |
| } |
| |
| @Override |
| protected void processProduces( |
| ProcessingEnvironment processingEnv, |
| TypeElement serviceType, |
| ExecutableElement method, |
| Set<String> produces) { |
| addMediaTypes(method, "produces", produces); |
| } |
| |
| @Override |
| protected void processConsumes( |
| ProcessingEnvironment processingEnv, |
| TypeElement serviceType, |
| ExecutableElement method, |
| Set<String> consumes) { |
| addMediaTypes(method, "consumes", consumes); |
| } |
| |
| private void addMediaTypes(ExecutableElement method, String annotationAttributeName, Set<String> mediaTypesSet) { |
| |
| AnnotationMirror mappingAnnotation = getMappingAnnotation(method); |
| |
| String[] mediaTypes = getAttribute(mappingAnnotation, annotationAttributeName); |
| |
| if (isNotEmpty(mediaTypes)) { |
| of(mediaTypes).forEach(mediaTypesSet::add); |
| } |
| } |
| |
| private AnnotationMirror getMappingAnnotation(Element element) { |
| return computeIfAbsent( |
| valueOf(element), |
| key -> filterFirst(getAllAnnotations(element), annotation -> { |
| DeclaredType annotationType = annotation.getAnnotationType(); |
| // try "@RequestMapping" first |
| if (REQUEST_MAPPING_ANNOTATION_CLASS_NAME.equals(annotationType.toString())) { |
| return true; |
| } |
| // try meta annotation |
| return isAnnotationPresent(annotationType.asElement(), REQUEST_MAPPING_ANNOTATION_CLASS_NAME); |
| })); |
| } |
| |
| private String getRequestPath(Element element) { |
| AnnotationMirror mappingAnnotation = getMappingAnnotation(element); |
| return getRequestPath(mappingAnnotation); |
| } |
| |
| private String getRequestPath(AnnotationMirror mappingAnnotation) { |
| // try "value" first |
| String[] value = getAttribute(mappingAnnotation, "value"); |
| |
| if (isEmpty(value)) { // try "path" later |
| value = getAttribute(mappingAnnotation, "path"); |
| } |
| |
| if (isEmpty(value)) { |
| return ""; |
| } |
| // TODO Is is required to support more request paths? |
| return value[FIRST_ELEMENT_INDEX]; |
| } |
| } |