Commit 837337c6 by qiuweili123

add dubbo swagger

parent 5970f716
...@@ -44,18 +44,6 @@ ...@@ -44,18 +44,6 @@
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId> <artifactId>fastjson</artifactId>
</dependency> </dependency>
......
...@@ -26,6 +26,7 @@ public class ApolloContextInitializer implements ApplicationContextInitializer<C ...@@ -26,6 +26,7 @@ public class ApolloContextInitializer implements ApplicationContextInitializer<C
if (!StringUtils.isEmpty(appId)) { if (!StringUtils.isEmpty(appId)) {
System.setProperty("app.id", appId); System.setProperty("app.id", appId);
System.setProperty("apollo.cacheDir", cacheDir); System.setProperty("apollo.cacheDir", cacheDir);
System.setProperty("apollo.bootstrap.enabled","true");
System.setProperty("apollo.bootstrap.eagerLoad.enabled","true"); System.setProperty("apollo.bootstrap.eagerLoad.enabled","true");
} }
} }
......
...@@ -34,7 +34,8 @@ ...@@ -34,7 +34,8 @@
<properties> <properties>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<dubbo.version>2.7.1</dubbo.version> <dubbo-starter.version>2.7.1</dubbo-starter.version>
<dubbo.version>2.7.2</dubbo.version>
<matrix.version>1.0.1-DEV-SNAPSHOT</matrix.version> <matrix.version>1.0.1-DEV-SNAPSHOT</matrix.version>
</properties> </properties>
...@@ -169,16 +170,31 @@ ...@@ -169,16 +170,31 @@
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>20.0</version> <version>20.0</version>
</dependency> </dependency>
<!--spring boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Aapche Dubbo --> <!-- Aapche Dubbo -->
<dependency> <dependency>
<groupId>org.apache.dubbo</groupId> <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId> <artifactId>dubbo-dependencies-bom</artifactId>
<version>${dubbo.version}</version> <version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-starter.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.dubbo</groupId> <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId> <artifactId>dubbo</artifactId>
<version>2.7.2</version> <version>${dubbo.version}</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
...@@ -226,11 +242,7 @@ ...@@ -226,11 +242,7 @@
<artifactId>swagger-bootstrap-ui</artifactId> <artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.3</version> <version>1.9.3</version>
</dependency> </dependency>
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>swagger-dubbo</artifactId>
<version>2.0.1</version>
</dependency>
<!--spring cloud--> <!--spring cloud-->
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
......
...@@ -29,10 +29,26 @@ ...@@ -29,10 +29,26 @@
<groupId>org.apache.dubbo</groupId> <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId> <artifactId>dubbo-spring-boot-starter</artifactId>
</dependency> </dependency>
<!--swagger2-->
<dependency> <dependency>
<groupId>com.deepoove</groupId> <groupId>io.springfox</groupId>
<artifactId>swagger-dubbo</artifactId> <artifactId>springfox-swagger2</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-core</artifactId>
<version>1.5.22</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>
\ No newline at end of file
package com.secoo.mall.config; package com.secoo.mall.common.config;
import com.deepoove.swagger.dubbo.annotations.EnableDubboSwagger;
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI; import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.secoo.mall.common.core.condition.ProdEnvCondition; import com.secoo.mall.common.core.condition.ProdEnvCondition;
import com.secoo.mall.dubbo.swagger.annotations.EnableDubboSwagger;
import com.secoo.mall.web.annotation.ApiController; import com.secoo.mall.web.annotation.ApiController;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -16,6 +16,8 @@ import springfox.documentation.spi.DocumentationType; ...@@ -16,6 +16,8 @@ import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2; import springfox.documentation.swagger2.annotations.EnableSwagger2;
;
@Configuration @Configuration
@EnableSwagger2 @EnableSwagger2
@EnableSwaggerBootstrapUI @EnableSwaggerBootstrapUI
...@@ -57,4 +59,5 @@ public class SwaggerConfig { ...@@ -57,4 +59,5 @@ public class SwaggerConfig {
.description("更多内容请关注:http://apims.siku.cn") .description("更多内容请关注:http://apims.siku.cn")
.build(); .build();
} }
} }
\ No newline at end of file
...@@ -38,6 +38,9 @@ public class ProtocolExceptionHandler { ...@@ -38,6 +38,9 @@ public class ProtocolExceptionHandler {
private String getMsg(BusinessException e) { private String getMsg(BusinessException e) {
if (messageSource == null) {
return e.getMsg();
}
return messageSource.getMessage(e.getMsg(), e.getArgs(), LocaleContextHolder.getLocale()); return messageSource.getMessage(e.getMsg(), e.getArgs(), LocaleContextHolder.getLocale());
} }
} }
package com.secoo.mall.dubbo.filter; package com.secoo.mall.dubbo.filter;
import com.secoo.mall.common.core.exception.BusinessException;
import com.secoo.mall.common.core.exception.ParameterException;
import com.secoo.mall.common.core.exception.SystemInternalException; import com.secoo.mall.common.core.exception.SystemInternalException;
import com.secoo.mall.common.handler.ProtocolExceptionHandler; import com.secoo.mall.common.handler.ProtocolExceptionHandler;
import com.secoo.mall.common.util.response.ResponseUtil; import com.secoo.mall.common.util.response.ResponseUtil;
...@@ -15,7 +17,7 @@ import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver; ...@@ -15,7 +17,7 @@ import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@Activate( @Activate(
group = {CommonConstants.PROVIDER} group = {CommonConstants.PROVIDER}, order = 1000
) )
public class DefaultExceptionFilter extends ExceptionFilter implements Filter { public class DefaultExceptionFilter extends ExceptionFilter implements Filter {
private Logger log = LoggerFactory.getLogger(DefaultExceptionFilter.class); private Logger log = LoggerFactory.getLogger(DefaultExceptionFilter.class);
...@@ -38,14 +40,12 @@ public class DefaultExceptionFilter extends ExceptionFilter implements Filter { ...@@ -38,14 +40,12 @@ public class DefaultExceptionFilter extends ExceptionFilter implements Filter {
@Override @Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) { public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
if (appResponse.hasException() && GenericService.class != invoker.getInterface()) { if (appResponse.hasException() && GenericService.class != invoker.getInterface()) {
Exception exception = getException(appResponse.getException());
Exception exception = (Exception) appResponse.getException();
Method method = resolver.resolveMethod(exception); Method method = resolver.resolveMethod(exception);
try { try {
Object realResult = method.invoke(this, exception); Object realResult = method.invoke(this, exception);
appResponse.setValue(realResult); appResponse.setValue(realResult);
} catch (Exception e) { } catch (Exception e) {
appResponse.setValue(ResponseUtil.getFailResponse(new SystemInternalException())); appResponse.setValue(ResponseUtil.getFailResponse(new SystemInternalException()));
} }
...@@ -59,6 +59,26 @@ public class DefaultExceptionFilter extends ExceptionFilter implements Filter { ...@@ -59,6 +59,26 @@ public class DefaultExceptionFilter extends ExceptionFilter implements Filter {
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e); logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
} }
private Exception getException(Throwable throwable) {
Exception exception = null;
String className = throwable.getClass().getSimpleName();
switch (className) {
case "BusinessException":
exception = (BusinessException) throwable;
break;
case "SystemInternalException":
exception = (SystemInternalException) throwable;
break;
case "ParameterException":
exception = (ParameterException) throwable;
break;
default:
exception = (Exception) throwable;
}
return exception;
}
} }
......
package com.secoo.mall.dubbo.swagger.annotations;
import org.springframework.context.annotation.ComponentScan;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = {java.lang.annotation.ElementType.TYPE})
@Documented
@ComponentScan(basePackages = {"com.secoo.mall.dubbo.swagger.config",
"com.secoo.mall.dubbo.swagger.controller", "com.secoo.mall.common.provider"})
public @interface EnableDubboSwagger {
}
package com.secoo.mall.dubbo.swagger.config;
import com.secoo.mall.dubbo.swagger.http.ReferenceManager;
import io.swagger.config.SwaggerConfig;
import io.swagger.models.Contact;
import io.swagger.models.Info;
import io.swagger.models.Swagger;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.ApplicationConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.ServletContext;
import java.text.MessageFormat;
@Component
public class DubboPropertyConfig implements SwaggerConfig {
@Value("${swagger.dubbo.application.version:}")
private String version;
@Value("${swagger.dubbo.application.groupId:}")
private String groupId;
@Value("${swagger.dubbo.application.artifactId:}")
private String artifactId;
@Autowired
private ServletContext servletContext;
private static String mavenDependency = "&lt;dependency&gt;<br/>"
+ "&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;{0}&lt;/groupId&gt;<br/>"
+ "&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;{1}&lt;/artifactId&gt;<br/>"
+ "&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;{2}&lt;/version&gt;<br/>"
+ "&lt;/dependency&gt;<br/>";
@Override
public Swagger configure(Swagger swagger) {
ApplicationConfig application = ReferenceManager.getInstance().getApplication();
if (null != application) {
Info info = swagger.getInfo();
if (info == null) {
info = new Info();
swagger.setInfo(info);
}
info.setTitle(application.getName());
version = StringUtils.isNotBlank(version) ? version : application.getVersion();
if (StringUtils.isNotBlank(groupId)
&& StringUtils.isNotBlank(artifactId)
&& StringUtils.isNotBlank(version)) {
info.setDescription(MessageFormat.format(mavenDependency, groupId, artifactId, version));
}
info.setVersion(StringUtils.isNotBlank(version) ? version : "");
Contact contact = new Contact();
info.setContact(contact);
contact.setName(application.getOwner());
}
setBashPath(swagger);
return swagger;
}
private void setBashPath(Swagger swagger) {
if (StringUtils.isEmpty(swagger.getBasePath())) {
swagger.setBasePath(StringUtils.isEmpty(servletContext.getContextPath()) ? "/" : servletContext.getContextPath());
}
}
@Override
public String getFilterClass() {
return null;
}
}
package com.secoo.mall.dubbo.swagger.config;
import com.secoo.mall.dubbo.swagger.http.ReferenceManager;
import io.swagger.config.Scanner;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Set;
@Component
public class DubboServiceScanner implements Scanner {
@Override
public Set<Class<?>> classes() {
return interfaceMapRef().keySet();
}
public Map<Class<?>, Object> interfaceMapRef() {
return ReferenceManager.getInstance().getInterfaceMapRef();
}
@Override
public boolean getPrettyPrint() {
return false;
}
@Override
public void setPrettyPrint(boolean shouldPrettyPrint) {
}
}
package com.secoo.mall.dubbo.swagger.config;
import io.swagger.models.Swagger;
import org.springframework.stereotype.Component;
@Component
public class SwaggerDocCache {
private Swagger swagger;
public Swagger getSwagger() {
return swagger;
}
public void setSwagger(Swagger swagger) {
this.swagger = swagger;
}
}
package com.secoo.mall.dubbo.swagger.controller;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.secoo.mall.dubbo.swagger.http.HttpMatch;
import com.secoo.mall.dubbo.swagger.http.ReferenceManager;
import com.secoo.mall.dubbo.swagger.reader.NameDiscover;
import io.swagger.annotations.Api;
import io.swagger.util.Json;
import io.swagger.util.PrimitiveType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map.Entry;
@Controller
@RequestMapping("${swagger.dubbo.http:h}")
@Api(hidden = true)
public class DubboHttpController {
private static Logger logger = LoggerFactory.getLogger(DubboHttpController.class);
private static final String CLUSTER_RPC = "rpc";
@Value("${swagger.dubbo.enable:true}")
private boolean enable = true;
@Value("${swagger.dubbo.cluster:rpc}")
private String cluster = CLUSTER_RPC;
@RequestMapping(value = "/{interfaceClass}/{methodName}", produces = "application/json; charset=utf-8")
@ResponseBody
public ResponseEntity<String> invokeDubbo(@PathVariable("interfaceClass") String interfaceClass,
@PathVariable("methodName") String methodName, HttpServletRequest request,
HttpServletResponse response) throws Exception {
return invokeDubbo(interfaceClass, methodName, null, request, response);
}
@RequestMapping(value = "/{interfaceClass}/{methodName}/{operationId}", produces = "application/json; charset=utf-8")
@ResponseBody
public ResponseEntity<String> invokeDubbo(@PathVariable("interfaceClass") String interfaceClass,
@PathVariable("methodName") String methodName,
@PathVariable("operationId") String operationId, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (!enable) {
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
Object ref = null;
Method method = null;
Object result = null;
Entry<Class<?>, Object> entry = ReferenceManager.getInstance().getRef(interfaceClass);
if (null == entry) {
logger.info("No Ref Service FOUND.");
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
ref = entry.getValue();
HttpMatch httpMatch = new HttpMatch(entry.getKey(), ref.getClass());
Method[] interfaceMethods = httpMatch.findInterfaceMethods(methodName);
if (null != interfaceMethods && interfaceMethods.length > 0) {
Method[] refMethods = httpMatch.findRefMethods(interfaceMethods, operationId,
request.getMethod());
method = httpMatch.matchRefMethod(refMethods, methodName, request.getParameterMap().keySet());
}
if (null == method) {
logger.info("No Service Method FOUND.");
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
String[] parameterNames = NameDiscover.parameterNameDiscover.getParameterNames(method);
logger.info("[Swagger-dubbo] Invoke by " + cluster);
if (CLUSTER_RPC.equals(cluster)) {
ref = ReferenceManager.getInstance().getProxy(interfaceClass);
if (null == ref) {
logger.info("No Ref Proxy Service FOUND.");
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
method = ref.getClass().getMethod(method.getName(), method.getParameterTypes());
if (null == method) {
logger.info("No Proxy Service Method FOUND.");
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
}
logger.debug("[Swagger-dubbo] Invoke dubbo service method:{},parameter:{}", method, Json.pretty(request.getParameterMap()));
if (null == parameterNames || parameterNames.length == 0) {
result = method.invoke(ref);
} else {
Object[] args = new Object[parameterNames.length];
Type[] parameterTypes = method.getGenericParameterTypes();
Class<?>[] parameterClazz = method.getParameterTypes();
for (int i = 0; i < parameterNames.length; i++) {
Object suggestPrameterValue = suggestPrameterValue(parameterTypes[i],
parameterClazz[i], request.getParameter(parameterNames[i]));
args[i] = suggestPrameterValue;
}
result = method.invoke(ref, args);
}
return ResponseEntity.ok(Json.mapper().writeValueAsString(result));
}
private Object suggestPrameterValue(Type type, Class<?> cls, String parameter)
throws JsonParseException, JsonMappingException, IOException {
PrimitiveType fromType = PrimitiveType.fromType(type);
if (null != fromType) {
DefaultConversionService service = new DefaultConversionService();
boolean actual = service.canConvert(String.class, cls);
if (actual) {
return service.convert(parameter, cls);
}
} else {
if (null == parameter) return null;
try {
return Json.mapper().readValue(parameter, cls);
} catch (Exception e) {
throw new IllegalArgumentException("The parameter value [" + parameter + "] should be json of [" + cls.getName() + "] Type.", e);
}
}
try {
return Class.forName(cls.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
package com.secoo.mall.dubbo.swagger.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.secoo.mall.dubbo.swagger.config.DubboPropertyConfig;
import com.secoo.mall.dubbo.swagger.config.DubboServiceScanner;
import com.secoo.mall.dubbo.swagger.config.SwaggerDocCache;
import com.secoo.mall.dubbo.swagger.reader.Reader;
import io.swagger.annotations.Api;
import io.swagger.config.SwaggerConfig;
import io.swagger.models.Swagger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import springfox.documentation.spring.web.json.Json;
import java.util.Map;
@Controller
@RequestMapping("${swagger.dubbo.doc:swagger-dubbo}")
@Api(hidden = true)
public class SwaggerDubboController {
public static final String DEFAULT_URL = "/api-docs";
private static final String HAL_MEDIA_TYPE = "application/hal+json";
@Autowired
private DubboServiceScanner dubboServiceScanner;
@Autowired
private DubboPropertyConfig dubboPropertyConfig;
@Autowired
private SwaggerDocCache swaggerDocCache;
@Value("${swagger.dubbo.http:h}")
private String httpContext;
@Value("${swagger.dubbo.enable:true}")
private boolean enable = true;
@RequestMapping(value = DEFAULT_URL,
method = RequestMethod.GET,
produces = {"application/json; charset=utf-8", HAL_MEDIA_TYPE})
@ResponseBody
public ResponseEntity<Json> getApiList() throws JsonProcessingException {
if (!enable) {
return new ResponseEntity<Json>(HttpStatus.NOT_FOUND);
}
Swagger swagger = swaggerDocCache.getSwagger();
if (null != swagger) {
return new ResponseEntity<Json>(new Json(io.swagger.util.Json.mapper().writeValueAsString(swagger)), HttpStatus.OK);
} else {
swagger = new Swagger();
}
final SwaggerConfig configurator = dubboPropertyConfig;
if (configurator != null) {
configurator.configure(swagger);
}
Map<Class<?>, Object> interfaceMapRef = dubboServiceScanner.interfaceMapRef();
if (null != interfaceMapRef) {
Reader.read(swagger, interfaceMapRef, httpContext);
}
swaggerDocCache.setSwagger(swagger);
return new ResponseEntity<Json>(new Json(io.swagger.util.Json.mapper().writeValueAsString(swagger)), HttpStatus.OK);
}
}
package com.secoo.mall.dubbo.swagger.http;
import com.secoo.mall.dubbo.swagger.reader.NameDiscover;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.HttpMethod;
import io.swagger.util.ReflectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.*;
public class HttpMatch {
private static Logger logger = LoggerFactory.getLogger(HttpMatch.class);
private Class<?> interfaceClass;
private Class<?> refClass;
public HttpMatch(Class<?> interfaceClass, Class<?> refClass) {
this.interfaceClass = interfaceClass;
this.refClass = refClass;
}
public Method[] findInterfaceMethods(String methodName) {
Method[] methods = interfaceClass.getMethods();
List<Method> ret = new ArrayList<Method>();
for (Method method : methods) {
if (method.getName().equals(methodName)) ret.add(method);
}
return ret.toArray(new Method[]{});
}
public Method[] findRefMethods(Method[] interfaceMethods, String operationId,
String requestMethod) {
List<Method> ret = new ArrayList<Method>();
for (Method method : interfaceMethods) {
Method m;
try {
m = refClass.getMethod(method.getName(), method.getParameterTypes());
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(m,
ApiOperation.class);
String nickname = null == apiOperation ? null : apiOperation.nickname();
if (operationId != null) {
if (!operationId.equals(nickname)) continue;
} else {
if (StringUtils.isNotBlank(nickname)) continue;
}
if (requestMethod != null) {
String httpMethod = null == apiOperation ? null : apiOperation.httpMethod();
if (StringUtils.isNotBlank(httpMethod) && !requestMethod.equals(httpMethod))
continue;
if (StringUtils.isBlank(httpMethod)
&& !requestMethod.equalsIgnoreCase(HttpMethod.POST.name()))
continue;
}
ret.add(m);
} catch (NoSuchMethodException e) {
logger.error("NoSuchMethodException", e);
} catch (SecurityException e) {
logger.error("SecurityException", e);
}
}
return ret.toArray(new Method[]{});
}
public Method matchRefMethod(Method[] refMethods, String methodName, Set<String> keySet) {
if (refMethods.length == 0) {
return null;
}
if (refMethods.length == 1) {
return refMethods[0];
}
List<RateMethod> rateMethods = new ArrayList<RateMethod>();
for (Method method : refMethods) {
String[] parameterNames = NameDiscover.parameterNameDiscover
.getParameterNames(method);
if (parameterNames == null) return method;
float correctRate = 0.0f;
int hit = 0;
int error = 0;
for (String paramName : parameterNames) {
if (keySet.contains(paramName)) hit++;
else error++;
}
correctRate = error / (float) hit;
rateMethods.add(new RateMethod(method, (int) correctRate * 100));
}
if (rateMethods.isEmpty()) return null;
Collections.sort(rateMethods, new Comparator<RateMethod>() {
@Override
public int compare(RateMethod o1, RateMethod o2) {
return o2.getRate() - o1.getRate();
}
});
return rateMethods.get(0).getMethod();
}
class RateMethod {
private Method method;
private int rate;
public RateMethod(Method method, int rate) {
this.method = method;
this.rate = rate;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public int getRate() {
return rate;
}
public void setRate(int rate) {
this.rate = rate;
}
}
}
package com.secoo.mall.dubbo.swagger.http;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.extension.SpringExtensionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class ReferenceManager {
private static Logger logger = LoggerFactory.getLogger(ReferenceManager.class);
@SuppressWarnings("rawtypes")
private static Collection<ServiceBean> services;
private static Map<Class<?>, Object> interfaceMapProxy = new ConcurrentHashMap<Class<?>, Object>();
private static Map<Class<?>, Object> interfaceMapRef = new ConcurrentHashMap<Class<?>, Object>();
private static ReferenceManager instance;
private static ApplicationConfig application;
private ReferenceManager() {
}
@SuppressWarnings({"rawtypes", "unchecked"})
public synchronized static ReferenceManager getInstance() {
if (null != instance) return instance;
instance = new ReferenceManager();
services = new HashSet<ServiceBean>();
try {
/* Field field = SpringExtensionFactory.class.getDeclaredField("contexts");
field.setAccessible(true);*/
Set<ApplicationContext> contexts = SpringExtensionFactory.getContexts();
for (ApplicationContext context : contexts) {
services.addAll(context.getBeansOfType(ServiceBean.class).values());
}
} catch (Exception e) {
logger.error("Get All Dubbo Service Error", e);
return instance;
}
for (ServiceBean<?> bean : services) {
interfaceMapRef.putIfAbsent(bean.getInterfaceClass(), bean.getRef());
}
//
if (!services.isEmpty()) {
ServiceBean<?> bean = services.toArray(new ServiceBean[]{})[0];
application = bean.getApplication();
}
return instance;
}
public Object getProxy(String interfaceClass) {
Set<Entry<Class<?>, Object>> entrySet = interfaceMapProxy.entrySet();
for (Entry<Class<?>, Object> entry : entrySet) {
if (entry.getKey().getName().equals(interfaceClass)) {
return entry.getValue();
}
}
for (ServiceBean<?> service : services) {
if (interfaceClass.equals(service.getInterfaceClass().getName())) {
ReferenceConfig<Object> reference = new ReferenceConfig<Object>();
reference.setApplication(service.getApplication());
reference.setRegistry(service.getRegistry());
reference.setRegistries(service.getRegistries());
reference.setInterface(service.getInterfaceClass());
reference.setVersion(service.getVersion());
interfaceMapProxy.put(service.getInterfaceClass(), reference.get());
return reference.get();
}
}
return null;
}
public Entry<Class<?>, Object> getRef(String interfaceClass) {
Set<Entry<Class<?>, Object>> entrySet = interfaceMapRef.entrySet();
for (Entry<Class<?>, Object> entry : entrySet) {
if (entry.getKey().getName().equals(interfaceClass)) {
return entry;
}
}
return null;
}
@SuppressWarnings("rawtypes")
public Collection<ServiceBean> getServices() {
return services;
}
public ApplicationConfig getApplication() {
return application;
}
public Map<Class<?>, Object> getInterfaceMapRef() {
return interfaceMapRef;
}
}
package com.secoo.mall.dubbo.swagger.reader;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.collect.Collections2;
import io.swagger.annotations.*;
import io.swagger.converter.ModelConverters;
import io.swagger.models.*;
import io.swagger.models.parameters.*;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.MapProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.RefProperty;
import io.swagger.util.*;
import org.apache.commons.lang3.StringUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;
public class DubboReaderExtension implements ReaderExtension {
@Override
public int getPriority() {
return 0;
}
@Override
public boolean isReadable(ReaderContext context) {
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
// read do not have Api annotation cls, skip hidden cls
return apiAnnotation == null || (context.isReadHidden() || !apiAnnotation.hidden());
}
@Override
public void applyConsumes(ReaderContext context, Operation operation, Method method) {
final List<String> consumes = new ArrayList<String>();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null) {
consumes.addAll(parseStringValues(apiOperation.consumes()));
}
if (consumes.isEmpty()) {
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiAnnotation != null) {
consumes.addAll(parseStringValues(apiAnnotation.consumes()));
}
consumes.addAll(context.getParentConsumes());
}
for (String consume : consumes) {
operation.consumes(consume);
}
}
private static List<String> parseStringValues(String str) {
return parseAnnotationValues(str, new Function<String, String>() {
@Override
public String apply(String value) {
return value;
}
});
}
private static <T> List<T> parseAnnotationValues(String str, Function<String, T> processor) {
final List<T> result = new ArrayList<T>();
for (String item : Splitter.on(",").trimResults().omitEmptyStrings().split(str)) {
result.add(processor.apply(item));
}
return result;
}
@Override
public void applyProduces(ReaderContext context, Operation operation, Method method) {
final List<String> produces = new ArrayList<String>();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null) {
produces.addAll(parseStringValues(apiOperation.produces()));
}
if (produces.isEmpty()) {
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiAnnotation != null) {
produces.addAll(parseStringValues(apiAnnotation.produces()));
}
produces.addAll(context.getParentProduces());
}
for (String produce : produces) {
operation.produces(produce);
}
}
@Override
public String getHttpMethod(ReaderContext context, Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
return apiOperation == null || StringUtils.isEmpty(apiOperation.httpMethod())
? HttpMethod.POST.name() : apiOperation.httpMethod();
}
@Override
public String getPath(ReaderContext context, Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
String operationId = null == apiOperation ? ""
: StringUtils.isBlank(apiOperation.nickname()) ? null : apiOperation.nickname();
return PathUtils.collectPath(context.getParentPath(), context.getInterfaceCls().getName(),
method.getName(), operationId);
}
@Override
public void applyOperationId(Operation operation, Method method) {
operation.operationId(method.getName());
}
@Override
public void applySummary(Operation operation, Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && StringUtils.isNotBlank(apiOperation.value())) {
operation.summary(apiOperation.value());
}
}
@Override
public void applyDescription(Operation operation, Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && StringUtils.isNotBlank(apiOperation.notes())) {
operation.description(apiOperation.notes());
}
operation.description(operation.getDescription() == null ? outputMethod(method)
: (outputMethod(method) + operation.getDescription()));
}
private String outputMethod(Method method) {
try {
Class<?>[] types = method.getParameterTypes();
Class<?>[] exceptionTypes = method.getExceptionTypes();
StringBuilder sb = new StringBuilder();
sb.append(method.getReturnType().getSimpleName()).append(" ").append(method.getName());
sb.append('(');
for (int j = 0; j < types.length; j++) {
sb.append(types[j].getName());
if (j < (types.length - 1)) sb.append(",");
}
sb.append(')');
if (exceptionTypes.length > 0) {
sb.append(" throws ");
for (int j = 0; j < exceptionTypes.length; j++) {
sb.append(exceptionTypes[j].getSimpleName());
if (j < (exceptionTypes.length - 1)) sb.append(",");
}
}
return sb.toString();
} catch (Exception e) {
return "<" + e + ">";
}
}
@Override
public void applySchemes(ReaderContext context, Operation operation, Method method) {
final List<Scheme> schemes = new ArrayList<Scheme>();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiOperation != null) {
schemes.addAll(parseSchemes(apiOperation.protocols()));
}
if (schemes.isEmpty() && apiAnnotation != null) {
schemes.addAll(parseSchemes(apiAnnotation.protocols()));
}
for (Scheme scheme : schemes) {
operation.scheme(scheme);
}
}
private static List<Scheme> parseSchemes(String schemes) {
final List<Scheme> result = new ArrayList<Scheme>();
for (String item : StringUtils.trimToEmpty(schemes).split(",")) {
final Scheme scheme = Scheme.forValue(StringUtils.trimToNull(item));
if (scheme != null && !result.contains(scheme)) {
result.add(scheme);
}
}
return result;
}
@Override
public void setDeprecated(Operation operation, Method method) {
operation.deprecated(ReflectionUtils.getAnnotation(method, Deprecated.class) != null);
}
@Override
public void applySecurityRequirements(ReaderContext context, Operation operation,
Method method) {
}
// @Override
// public void applyTags(ReaderContext context) {
// final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
// if (apiAnnotation != null) {
// Tag tag = new
// Tag().name(context.getInterfaceCls().getSimpleName()).description(apiAnnotation.value());
// }
// }
@Override
public void applyTags(ReaderContext context, Operation operation, Method method) {
final List<String> tags = new ArrayList<String>();
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiAnnotation != null) {
Collection<String> filter = Collections2.filter(Arrays.asList(apiAnnotation.tags()),
new Predicate<String>() {
@Override
public boolean apply(String input) {
return StringUtils.isNotBlank(input);
}
});
if (filter.isEmpty()) {
tags.add(context.getInterfaceCls().getSimpleName());
} else {
tags.addAll(filter);
}
} else {
tags.add(context.getInterfaceCls().getSimpleName());
}
tags.addAll(context.getParentTags());
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null) {
tags.addAll(Collections2.filter(Arrays.asList(apiOperation.tags()),
new Predicate<String>() {
@Override
public boolean apply(String input) {
return StringUtils.isNotBlank(input);
}
}));
}
for (String tag : tags) {
operation.tag(tag);
}
}
private static final String SUCCESSFUL_OPERATION = "";
private static boolean isValidResponse(Type type) {
final JavaType javaType = TypeFactory.defaultInstance().constructType(type);
return !ReflectionUtils.isVoid(javaType);
}
private static Map<String, Property> parseResponseHeaders(ReaderContext context,
ResponseHeader[] headers) {
Map<String, Property> responseHeaders = null;
for (ResponseHeader header : headers) {
final String name = header.name();
if (StringUtils.isNotEmpty(name)) {
if (responseHeaders == null) {
responseHeaders = new HashMap<String, Property>();
}
final Class<?> cls = header.response();
if (!ReflectionUtils.isVoid(cls)) {
final Property property = ModelConverters.getInstance().readAsProperty(cls);
if (property != null) {
final Property responseProperty = ContainerWrapper.wrapContainer(
header.responseContainer(), property, ContainerWrapper.ARRAY,
ContainerWrapper.LIST, ContainerWrapper.SET);
responseProperty.setDescription(header.description());
responseHeaders.put(name, responseProperty);
appendModels(context.getSwagger(), cls);
}
}
}
}
return responseHeaders;
}
private static void appendModels(Swagger swagger, Type type) {
final Map<String, Model> models = ModelConverters.getInstance().readAll(type);
for (Map.Entry<String, Model> entry : models.entrySet()) {
swagger.model(entry.getKey(), entry.getValue());
}
}
private static Type getResponseType(Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && !ReflectionUtils.isVoid(apiOperation.response())) {
return apiOperation.response();
} else {
return method.getGenericReturnType();
}
}
private static String getResponseContainer(ApiOperation apiOperation) {
return apiOperation == null ? null
: StringUtils.defaultIfBlank(apiOperation.responseContainer(), null);
}
@Override
public void applyResponses(ReaderContext context, Operation operation, Method method) {
final Map<Integer, Response> result = new HashMap<Integer, Response>();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && StringUtils.isNotBlank(apiOperation.responseReference())) {
final Response response = new Response().description(SUCCESSFUL_OPERATION);
response.schema(new RefProperty(apiOperation.responseReference()));
result.put(apiOperation.code(), response);
}
final Type responseType = getResponseType(method);
if (isValidResponse(responseType)) {
final Property property = ModelConverters.getInstance().readAsProperty(responseType);
if (property != null) {
final Property responseProperty = ContainerWrapper
.wrapContainer(getResponseContainer(apiOperation), property);
final int responseCode = apiOperation == null ? 200 : apiOperation.code();
final Map<String, Property> defaultResponseHeaders = apiOperation == null
? Collections.<String, Property>emptyMap()
: parseResponseHeaders(context, apiOperation.responseHeaders());
final Response response = new Response().description(SUCCESSFUL_OPERATION)
.schema(responseProperty).headers(defaultResponseHeaders);
result.put(responseCode, response);
appendModels(context.getSwagger(), responseType);
}
}
final ApiResponses responseAnnotation = ReflectionUtils.getAnnotation(method,
ApiResponses.class);
if (responseAnnotation != null) {
for (ApiResponse apiResponse : responseAnnotation.value()) {
final Map<String, Property> responseHeaders = parseResponseHeaders(context,
apiResponse.responseHeaders());
final Response response = new Response().description(apiResponse.message())
.headers(responseHeaders);
if (StringUtils.isNotEmpty(apiResponse.reference())) {
response.schema(new RefProperty(apiResponse.reference()));
} else if (!ReflectionUtils.isVoid(apiResponse.response())) {
final Type type = apiResponse.response();
final Property property = ModelConverters.getInstance().readAsProperty(type);
if (property != null) {
response.schema(ContainerWrapper
.wrapContainer(apiResponse.responseContainer(), property));
appendModels(context.getSwagger(), type);
}
}
result.put(apiResponse.code(), response);
}
}
for (Map.Entry<Integer, Response> responseEntry : result.entrySet()) {
if (responseEntry.getKey() == 0) {
operation.defaultResponse(responseEntry.getValue());
} else {
operation.response(responseEntry.getKey(), responseEntry.getValue());
}
}
}
@Override
public void applyParameters(ReaderContext context, Operation operation, Type type,
Annotation[] annotations) {
}
private void applyParametersV2(ReaderContext context, Operation operation, String name,
Type type, Class<?> cls, Annotation[] annotations, Annotation[] interfaceParamAnnotations) {
Annotation apiParam = null;
if (annotations != null) {
for (Annotation annotation : interfaceParamAnnotations) {
if (annotation instanceof ApiParam) {
apiParam = annotation;
break;
}
}
if (null == apiParam) {
for (Annotation annotation : annotations) {
if (annotation instanceof ApiParam) {
apiParam = annotation;
break;
}
}
}
}
final Parameter parameter = readParam(context.getSwagger(), type, cls,
null == apiParam ? null : (ApiParam) apiParam);
if (parameter != null) {
parameter.setName(null == name ? parameter.getName() : name);
operation.parameter(parameter);
}
}
private Parameter readParam(Swagger swagger, Type type, Class<?> cls, ApiParam param) {
PrimitiveType fromType = PrimitiveType.fromType(type);
final Parameter para = null == fromType ? new BodyParameter() : new QueryParameter();
Parameter parameter = ParameterProcessor.applyAnnotations(swagger, para,
type == null ? String.class : type, null == param ? new ArrayList<Annotation>()
: Collections.<Annotation>singletonList(param));
if (parameter instanceof AbstractSerializableParameter) {
final AbstractSerializableParameter<?> p = (AbstractSerializableParameter<?>) parameter;
if (p.getType() == null) p.setType(null == fromType ? "string" : fromType.getCommonName());
p.setRequired(p.getRequired() == true ? true : cls.isPrimitive());
} else {
//hack: Get the from data model paramter from BodyParameter
BodyParameter bp = (BodyParameter) parameter;
bp.setIn("formData");
}
return parameter;
}
@Override
public void applyParameters(ReaderContext context, Operation operation, Method method,
Method interfaceMethod) {
try {
String[] parameterNames = NameDiscover.parameterNameDiscover.getParameterNames(method);
Type[] genericParameterTypes = method.getGenericParameterTypes();
Class<?>[] parameterTypes = method.getParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Annotation[][] interfaceParamAnnotations = interfaceMethod.getParameterAnnotations();
for (int i = 0; i < genericParameterTypes.length; i++) {
applyParametersV2(context, operation,
null == parameterNames ? null : parameterNames[i], genericParameterTypes[i], parameterTypes[i],
parameterAnnotations[i], interfaceParamAnnotations[i]);
}
} catch (SecurityException e) {
e.printStackTrace();
}
}
@Override
public void applyImplicitParameters(ReaderContext context, Operation operation, Method method) {
final ApiImplicitParams implicitParams = method.getAnnotation(ApiImplicitParams.class);
if (implicitParams != null && implicitParams.value().length > 0) {
for (ApiImplicitParam param : implicitParams.value()) {
final Parameter p = readImplicitParam(context.getSwagger(), param);
if (p != null) {
operation.parameter(p);
}
}
}
}
private Parameter readImplicitParam(Swagger swagger, ApiImplicitParam param) {
PrimitiveType fromType = PrimitiveType.fromName(param.paramType());
final Parameter p = null == fromType ? new FormParameter() : new QueryParameter();
final Type type = ReflectionUtils.typeFromString(param.dataType());
return ParameterProcessor.applyAnnotations(swagger, p, type == null ? String.class : type,
Collections.<Annotation>singletonList(param));
}
@Override
public void applyExtensions(ReaderContext context, Operation operation, Method method) {
final ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
if (apiOperation != null) {
operation.getVendorExtensions()
.putAll(BaseReaderUtils.parseExtensions(apiOperation.extensions()));
}
}
enum ContainerWrapper {
LIST("list") {
@Override
protected Property doWrap(Property property) {
return new ArrayProperty(property);
}
},
ARRAY("array") {
@Override
protected Property doWrap(Property property) {
return new ArrayProperty(property);
}
},
MAP("map") {
@Override
protected Property doWrap(Property property) {
return new MapProperty(property);
}
},
SET("set") {
@Override
protected Property doWrap(Property property) {
ArrayProperty arrayProperty = new ArrayProperty(property);
arrayProperty.setUniqueItems(true);
return arrayProperty;
}
};
private final String container;
ContainerWrapper(String container) {
this.container = container;
}
public static Property wrapContainer(String container, Property property,
ContainerWrapper... allowed) {
final Set<ContainerWrapper> tmp = allowed.length > 0
? EnumSet.copyOf(Arrays.asList(allowed))
: EnumSet.allOf(ContainerWrapper.class);
for (ContainerWrapper wrapper : tmp) {
final Property prop = wrapper.wrap(container, property);
if (prop != null) {
return prop;
}
}
return property;
}
public Property wrap(String container, Property property) {
if (this.container.equalsIgnoreCase(container)) {
return doWrap(property);
}
return null;
}
protected abstract Property doWrap(Property property);
}
}
package com.secoo.mall.dubbo.swagger.reader;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.PrioritizedParameterNameDiscoverer;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
public final class NameDiscover {
public static final ParameterNameDiscoverer parameterNameDiscover;
static {
parameterNameDiscover = new PrioritizedParameterNameDiscoverer();
((PrioritizedParameterNameDiscoverer) parameterNameDiscover)
.addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
((PrioritizedParameterNameDiscoverer) parameterNameDiscover)
.addDiscoverer(new StandardReflectionParameterNameDiscoverer());
}
}
package com.secoo.mall.dubbo.swagger.reader;
import io.swagger.annotations.Info;
import io.swagger.annotations.*;
import io.swagger.models.Contact;
import io.swagger.models.ExternalDocs;
import io.swagger.models.License;
import io.swagger.models.Tag;
import io.swagger.models.*;
import io.swagger.models.auth.In;
import io.swagger.models.parameters.Parameter;
import io.swagger.util.BaseReaderUtils;
import io.swagger.util.PathUtils;
import io.swagger.util.ReflectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Map.Entry;
/**
* The <code>Reader</code> class scans classes for Swagger annotations.
*/
public class Reader {
private final Swagger swagger;
private Reader(Swagger swagger) {
this.swagger = swagger;
}
/**
* Scans a set of classes for Swagger annotations.
*
* @param swagger is the Swagger instance
* @param classes are a set of classes to scan
*/
public static void read(Swagger swagger, Set<Class<?>> classes) {
final Reader reader = new Reader(swagger);
for (Class<?> cls : classes) {
final ReaderContext context = new ReaderContext(swagger, cls, cls, "", null, false,
new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(),
new ArrayList<Parameter>());
reader.read(context);
}
}
public static void read(Swagger swagger, Map<Class<?>, Object> interfaceMapRef,
String httpContext) {
final Reader reader = new Reader(swagger);
List<Entry<Class<?>, Object>> arrayList = new ArrayList<Entry<Class<?>, Object>>(interfaceMapRef.entrySet());
Collections.sort(arrayList, new Comparator<Entry<Class<?>, Object>>() {
@Override
public int compare(Entry<Class<?>, Object> o1, Entry<Class<?>, Object> o2) {
return o1.getKey().getSimpleName().compareTo(o2.getKey().getSimpleName());
}
});
for (Entry<Class<?>, Object> entry : arrayList) {
final ReaderContext context = new ReaderContext(swagger,
entry.getValue().getClass(), entry.getKey(), httpContext, null, false,
new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(),
new ArrayList<Parameter>());
reader.read(context);
}
}
private void read(ReaderContext context) {
final SwaggerDefinition swaggerDefinition = context.getCls()
.getAnnotation(SwaggerDefinition.class);
if (swaggerDefinition != null) {
readSwaggerConfig(swaggerDefinition);
}
List<Method> interfaceMethodList = Arrays.asList(context.getInterfaceCls().getMethods());
Collections.sort(interfaceMethodList, new Comparator<Method>() {
@Override
public int compare(Method o1, Method o2) {
return o1.getName().compareTo(o2.getName());
}
});
Map<Method, Method> serviceMethods = new LinkedHashMap<Method, Method>();
for (Method method : interfaceMethodList) {
if (!ReflectionUtils.isOverriddenMethod(method, context.getCls())) {
serviceMethods.put(method, getRefMethod(context, method));
}
}
for (Entry<Method, Method> entry : serviceMethods.entrySet()) {
Method method = entry.getValue();
Method interfaceMethod = entry.getKey();
final Operation operation = new Operation();
String operationPath = null;
String httpMethod = null;
ReaderExtension extension = new DubboReaderExtension();
if (operationPath == null) {
operationPath = extension.getPath(context, method);
}
if (httpMethod == null) {
httpMethod = extension.getHttpMethod(context, method);
}
if (operationPath == null || httpMethod == null) {
continue;
}
if (extension.isReadable(context)) {
extension.setDeprecated(operation, method);
extension.applyConsumes(context, operation, method);
extension.applyProduces(context, operation, method);
extension.applyOperationId(operation, method);
extension.applySummary(operation, method);
extension.applyDescription(operation, method);
extension.applySchemes(context, operation, method);
extension.applySecurityRequirements(context, operation, method);
extension.applyTags(context, operation, method);
extension.applyResponses(context, operation, method);
extension.applyImplicitParameters(context, operation, method);
extension.applyParameters(context, operation, method, interfaceMethod);
extension.applyExtensions(context, operation, method);
}
if (httpMethod != null) {
if (operation.getResponses() == null) {
operation.defaultResponse(new Response().description("successful operation"));
}
final Map<String, String> regexMap = new HashMap<String, String>();
final String parsedPath = PathUtils.parsePath(operationPath, regexMap);
Path path = swagger.getPath(parsedPath);
if (path == null) {
path = new Path();
swagger.path(parsedPath, path);
}
path.set(httpMethod.toLowerCase(), operation);
}
}
}
private Method getRefMethod(ReaderContext context, Method method) {
try {
return context.getCls().getMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return method;
}
@SuppressWarnings("deprecation")
private void readSwaggerConfig(SwaggerDefinition config) {
readInfoConfig(config);
if (StringUtils.isNotBlank(config.basePath())) {
swagger.setBasePath(config.basePath());
}
if (StringUtils.isNotBlank(config.host())) {
swagger.setHost(config.host());
}
for (String consume : config.consumes()) {
if (StringUtils.isNotBlank(consume)) {
swagger.addConsumes(consume);
}
}
for (String produce : config.produces()) {
if (StringUtils.isNotBlank(produce)) {
swagger.addProduces(produce);
}
}
if (StringUtils.isNotBlank(config.externalDocs().value())) {
ExternalDocs externalDocs = swagger.getExternalDocs();
if (externalDocs == null) {
externalDocs = new ExternalDocs();
swagger.setExternalDocs(externalDocs);
}
externalDocs.setDescription(config.externalDocs().value());
if (StringUtils.isNotBlank(config.externalDocs().url())) {
externalDocs.setUrl(config.externalDocs().url());
}
}
for (OAuth2Definition oAuth2Config : config.securityDefinition().oAuth2Definitions()) {
io.swagger.models.auth.OAuth2Definition oAuth2Definition = new io.swagger.models.auth.OAuth2Definition();
OAuth2Definition.Flow flow = oAuth2Config.flow();
if (flow.equals(OAuth2Definition.Flow.ACCESS_CODE)) {
oAuth2Definition = oAuth2Definition.accessCode(oAuth2Config.authorizationUrl(),
oAuth2Config.tokenUrl());
} else if (flow.equals(OAuth2Definition.Flow.APPLICATION)) {
oAuth2Definition = oAuth2Definition.application(oAuth2Config.tokenUrl());
} else if (flow.equals(OAuth2Definition.Flow.IMPLICIT)) {
oAuth2Definition = oAuth2Definition.implicit(oAuth2Config.authorizationUrl());
} else {
oAuth2Definition = oAuth2Definition.password(oAuth2Config.tokenUrl());
}
for (Scope scope : oAuth2Config.scopes()) {
oAuth2Definition.addScope(scope.name(), scope.description());
}
oAuth2Definition.setDescription(oAuth2Config.description());
swagger.addSecurityDefinition(oAuth2Config.key(), oAuth2Definition);
}
for (ApiKeyAuthDefinition[] apiKeyAuthConfigs : new ApiKeyAuthDefinition[][]{
config.securityDefinition().apiKeyAuthDefintions(),
config.securityDefinition().apiKeyAuthDefinitions()}) {
for (ApiKeyAuthDefinition apiKeyAuthConfig : apiKeyAuthConfigs) {
io.swagger.models.auth.ApiKeyAuthDefinition apiKeyAuthDefinition = new io.swagger.models.auth.ApiKeyAuthDefinition();
apiKeyAuthDefinition.setName(apiKeyAuthConfig.name());
apiKeyAuthDefinition.setIn(In.forValue(apiKeyAuthConfig.in().toValue()));
apiKeyAuthDefinition.setDescription(apiKeyAuthConfig.description());
swagger.addSecurityDefinition(apiKeyAuthConfig.key(), apiKeyAuthDefinition);
}
}
for (BasicAuthDefinition[] basicAuthConfigs : new BasicAuthDefinition[][]{
config.securityDefinition().basicAuthDefinions(),
config.securityDefinition().basicAuthDefinitions()}) {
for (BasicAuthDefinition basicAuthConfig : basicAuthConfigs) {
io.swagger.models.auth.BasicAuthDefinition basicAuthDefinition = new io.swagger.models.auth.BasicAuthDefinition();
basicAuthDefinition.setDescription(basicAuthConfig.description());
swagger.addSecurityDefinition(basicAuthConfig.key(), basicAuthDefinition);
}
}
for (io.swagger.annotations.Tag tagConfig : config.tags()) {
if (StringUtils.isNotBlank(tagConfig.name())) {
final Tag tag = new Tag();
tag.setName(tagConfig.name());
tag.setDescription(tagConfig.description());
if (StringUtils.isNotBlank(tagConfig.externalDocs().value())) {
tag.setExternalDocs(new ExternalDocs(tagConfig.externalDocs().value(),
tagConfig.externalDocs().url()));
}
tag.getVendorExtensions()
.putAll(BaseReaderUtils.parseExtensions(tagConfig.extensions()));
swagger.addTag(tag);
}
}
for (SwaggerDefinition.Scheme scheme : config.schemes()) {
if (scheme != SwaggerDefinition.Scheme.DEFAULT) {
swagger.addScheme(Scheme.forValue(scheme.name()));
}
}
}
private void readInfoConfig(SwaggerDefinition config) {
final Info infoConfig = config.info();
io.swagger.models.Info info = swagger.getInfo();
if (info == null) {
info = new io.swagger.models.Info();
swagger.setInfo(info);
}
if (StringUtils.isNotBlank(infoConfig.description())) {
info.setDescription(infoConfig.description());
}
if (StringUtils.isNotBlank(infoConfig.termsOfService())) {
info.setTermsOfService(infoConfig.termsOfService());
}
if (StringUtils.isNotBlank(infoConfig.title())) {
info.setTitle(infoConfig.title());
}
if (StringUtils.isNotBlank(infoConfig.version())) {
info.setVersion(infoConfig.version());
}
if (StringUtils.isNotBlank(infoConfig.contact().name())) {
Contact contact = info.getContact();
if (contact == null) {
contact = new Contact();
info.setContact(contact);
}
contact.setName(infoConfig.contact().name());
if (StringUtils.isNotBlank(infoConfig.contact().email())) {
contact.setEmail(infoConfig.contact().email());
}
if (StringUtils.isNotBlank(infoConfig.contact().url())) {
contact.setUrl(infoConfig.contact().url());
}
}
if (StringUtils.isNotBlank(infoConfig.license().name())) {
License license = info.getLicense();
if (license == null) {
license = new License();
info.setLicense(license);
}
license.setName(infoConfig.license().name());
if (StringUtils.isNotBlank(infoConfig.license().url())) {
license.setUrl(infoConfig.license().url());
}
}
info.getVendorExtensions().putAll(BaseReaderUtils.parseExtensions(infoConfig.extensions()));
}
}
package com.secoo.mall.dubbo.swagger.reader;
import io.swagger.models.Swagger;
import io.swagger.models.parameters.Parameter;
import java.util.List;
/**
* The <code>ReaderContext</code> class is wrapper for the <code>Reader</code>
* parameters.
*/
public class ReaderContext {
private Swagger swagger;
private Class<?> refCls;
private Class<?> interfaceCls;
private String parentPath;
private String parentHttpMethod;
private boolean readHidden;
private List<String> parentConsumes;
private List<String> parentProduces;
private List<String> parentTags;
private List<Parameter> parentParameters;
public ReaderContext(Swagger swagger, Class<?> refCls, Class<?> interfaceCls, String parentPath,
String parentHttpMethod, boolean readHidden, List<String> parentConsumes,
List<String> parentProduces, List<String> parentTags,
List<Parameter> parentParameters) {
setSwagger(swagger);
setRefCls(refCls);
setInterfaceCls(interfaceCls);
setParentPath(parentPath);
setParentHttpMethod(parentHttpMethod);
setReadHidden(readHidden);
setParentConsumes(parentConsumes);
setParentProduces(parentProduces);
setParentTags(parentTags);
setParentParameters(parentParameters);
}
public Swagger getSwagger() {
return swagger;
}
public void setSwagger(Swagger swagger) {
this.swagger = swagger;
}
public Class<?> getRefCls() {
return refCls;
}
public void setRefCls(Class<?> cls) {
this.refCls = cls;
}
public Class<?> getInterfaceCls() {
return interfaceCls;
}
public Class<?> getCls() {
return refCls;
}
public void setInterfaceCls(Class<?> interfaceCls) {
this.interfaceCls = interfaceCls;
}
public String getParentPath() {
return parentPath;
}
public void setParentPath(String parentPath) {
this.parentPath = parentPath;
}
public String getParentHttpMethod() {
return parentHttpMethod;
}
public void setParentHttpMethod(String parentHttpMethod) {
this.parentHttpMethod = parentHttpMethod;
}
public boolean isReadHidden() {
return readHidden;
}
public void setReadHidden(boolean readHidden) {
this.readHidden = readHidden;
}
public List<String> getParentConsumes() {
return parentConsumes;
}
public void setParentConsumes(List<String> parentConsumes) {
this.parentConsumes = parentConsumes;
}
public List<String> getParentProduces() {
return parentProduces;
}
public void setParentProduces(List<String> parentProduces) {
this.parentProduces = parentProduces;
}
public List<String> getParentTags() {
return parentTags;
}
public void setParentTags(List<String> parentTags) {
this.parentTags = parentTags;
}
public List<Parameter> getParentParameters() {
return parentParameters;
}
public void setParentParameters(List<Parameter> parentParameters) {
this.parentParameters = parentParameters;
}
}
package com.secoo.mall.dubbo.swagger.reader;
import io.swagger.models.Operation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
/**
* The <code>ReaderExtension</code> interface defines methods for scans resources for Swagger.
*/
public interface ReaderExtension {
/**
* Returns this extension's priority.
* Note: Extension will be executed first with lowest priority.
*
* @return this extension's priority
*/
int getPriority();
/**
* Checks that a resource should be scanned.
*
* @param context is the resource context
* @return true if the resource needs to be scanned, otherwise false
*/
boolean isReadable(ReaderContext context);
/**
* Reads the consumes from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyConsumes(ReaderContext context, Operation operation, Method method);
/**
* Reads the produces from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyProduces(ReaderContext context, Operation operation, Method method);
/**
* Returns http method.
*
* @param context is the resource context
* @param method is the method for reading annotations
* @return http method
*/
String getHttpMethod(ReaderContext context, Method method);
/**
* Returns operation's path.
*
* @param context is the resource context
* @param method is the method for reading annotations
* @return operation's path
*/
String getPath(ReaderContext context, Method method);
/**
* Reads the operation id from the method's annotations and applies it to the operation.
*
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyOperationId(Operation operation, Method method);
/**
* Reads the summary from the method's annotations and applies it to the operation.
*
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applySummary(Operation operation, Method method);
/**
* Reads the description from the method's annotations and applies it to the operation.
*
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyDescription(Operation operation, Method method);
/**
* Reads the schemes from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applySchemes(ReaderContext context, Operation operation, Method method);
/**
* Sets the deprecated flag to the operation.
*
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void setDeprecated(Operation operation, Method method);
/**
* Reads the security requirement from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applySecurityRequirements(ReaderContext context, Operation operation, Method method);
/**
* Reads the tags from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyTags(ReaderContext context, Operation operation, Method method);
/**
* Reads the responses from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyResponses(ReaderContext context, Operation operation, Method method);
/**
* Reads the parameters from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param type is the type of parameter
* @param annotations are the method's annotations
*/
void applyParameters(ReaderContext context, Operation operation, Type type, Annotation[] annotations);
/**
* Reads the implicit parameters from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyImplicitParameters(ReaderContext context, Operation operation, Method method);
/**
* Reads the extensions from the method's annotations and applies these to the operation.
*
* @param context is the resource context
* @param operation is the container for the operation data
* @param method is the method for reading annotations
*/
void applyExtensions(ReaderContext context, Operation operation, Method method);
void applyParameters(ReaderContext context, Operation operation, Method method,
Method interfaceMethod);
}
...@@ -15,7 +15,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; ...@@ -15,7 +15,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@RestControllerAdvice(annotations = ApiController.class) @RestControllerAdvice(annotations = ApiController.class)
public class ControllerResponseAdvice extends ProtocolExceptionHandler implements ResponseBodyAdvice<Object> { public class ControllerResponseAdvice extends ProtocolExceptionHandler implements ResponseBodyAdvice<Object> {
@Override @Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) { public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return AnnotationUtils.findAnnotation(methodParameter.getMethod(), ApiIgnoreJson.class) == null; return AnnotationUtils.findAnnotation(methodParameter.getMethod(), ApiIgnoreJson.class) == null;
......
com.secoo.mall.dubbo.filter.DefaultExceptionFilter exception=com.secoo.mall.dubbo.filter.DefaultExceptionFilter
\ No newline at end of file \ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment