com.github.liuanxin.api.util.WebUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of api-document Show documentation
Show all versions of api-document Show documentation
Collect api document, support to spring mvc
package com.github.liuanxin.api.util;
import com.github.liuanxin.api.annotation.*;
import com.github.liuanxin.api.constant.ApiConst;
import com.github.liuanxin.api.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.pattern.PathPattern;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.*;
public final class WebUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(WebUtil.class);
public static List getProjects(Map projectMap) {
if (Tools.isNotEmpty(projectMap)) {
List returnList = new ArrayList<>();
ThreadPoolExecutor executor = threadPool(projectMap.size());
List> futureList = new ArrayList<>();
for (Map.Entry entry : projectMap.entrySet()) {
final String name = entry.getKey();
final String url = entry.getValue();
if (Tools.isNotEmpty(name) && Tools.isNotEmpty(url)) {
futureList.add(executor.submit(new Callable() {
@Override
public DocumentInfo call() {
String uri = url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
String requestInfo = HttpUtil.get(uri + ApiConst.URL_PREFIX + ApiConst.URL_INFO);
ReturnInfo projectInfo = Tools.toObject(requestInfo, ReturnInfo.class);
return Tools.isNull(projectInfo) ? null : projectInfo.fillModule(name, url);
}
}));
}
}
for (Future future : futureList) {
try {
DocumentInfo info = future.get();
if (Tools.isNotNull(info)) {
returnList.add(info);
}
} catch (InterruptedException | ExecutionException ignore) {
}
}
return returnList;
} else {
return Collections.emptyList();
}
}
public static ThreadPoolExecutor threadPool(int size) {
int cpus = Runtime.getRuntime().availableProcessors();
int pool;
BlockingQueue queue;
if (size > cpus) {
pool = cpus;
queue = new LinkedBlockingQueue<>(size - cpus);
} else {
pool = size;
queue = new SynchronousQueue<>();
}
return new ThreadPoolExecutor(pool, pool, 60L, TimeUnit.SECONDS, queue);
}
public static DocumentCopyright copyright(DocumentCopyright copyright, List moduleList) {
if (Tools.isNotEmpty(moduleList)) {
int apiCount = 0;
for (DocumentModule module : moduleList) {
apiCount += module.getUrlList().size();
}
copyright.setGroupCount(moduleList.size()).setApiCount(apiCount);
}
return copyright;
}
public static DocumentInfoAndUrlMap infoAndUrlMap(RequestMappingHandlerMapping mapping,
DocumentCopyright copyright) {
Map moduleMap = Tools.newLinkedHashMap();
Map documentMap = Tools.newLinkedHashMap();
// meta info
boolean globalCommentInReturn = copyright.isCommentInReturnExample();
boolean globalRecordLevel = copyright.isReturnRecordLevel();
Map handlerMethods = mapping.getHandlerMethods();
for (Map.Entry entry : handlerMethods.entrySet()) {
RequestMappingInfo requestMapping = entry.getKey();
HandlerMethod handlerMethod = entry.getValue();
if (Tools.isNotNull(requestMapping) && Tools.isNotNull(handlerMethod) && wasJsonApi(handlerMethod)) {
ApiIgnore ignore = getAnnotation(handlerMethod, ApiIgnore.class);
if (Tools.isNull(ignore) || !ignore.value()) {
Set urlSet = getUrl(requestMapping);
Set methodSet = getMethod(requestMapping);
if (!ignoreUrl(urlSet, methodSet, copyright.getIgnoreUrlSet())) {
String method = handlerMethod.toString();
boolean innerRequestBody = hasInnerParamRequestBody(handlerMethod);
DocumentUrl document = new DocumentUrl();
// url
document.setUrl(String.join(", ", urlSet));
// method : get, post, put...
document.setMethod(String.join(", ", methodSet));
// param
List paramList;
if (innerRequestBody) {
String paramType = ReturnType.getRequestBodyParamTypeByMethod(handlerMethod);
String requestBodyJson = ReturnHandler.handlerReturnJson(method, paramType);
List requestBodyParamList = ReturnHandler.handlerReturn(method, paramType);
document.setRequestBodyJson(DocumentUrl.commentJson(requestBodyJson, true, true, requestBodyParamList));
paramList = new ArrayList<>();
} else {
paramList = ParamHandler.handlerParam(handlerMethod);
}
// no annotation: use global, annotation is false: not use, annotation is true: use self
ApiTokens apiTokens = getAnnotation(handlerMethod, ApiTokens.class);
if (Tools.isNull(apiTokens)) {
document.setUseGlobalParam("1");
} else {
document.setUseGlobalParam(apiTokens.useGlobal() ? "1" : ApiConst.EMPTY);
List extraParams = new LinkedList<>();
for (ApiToken token : apiTokens.value()) {
extraParams.add(DocumentParam.buildToken(token));
}
if (extraParams.size() > 0) {
paramList.addAll(0, extraParams);
}
}
document.setRequestBody(hasRequestBody(handlerMethod) ? "1" : ApiConst.EMPTY);
document.setBasicParamRequestBody(hasBasicParamRequestBody(handlerMethod) ? "1" : ApiConst.EMPTY);
document.setInnerParamRequestBody(innerRequestBody ? "1" : ApiConst.EMPTY);
document.setParamList(paramList);
ApiMethod apiMethod = handlerMethod.getMethodAnnotation(ApiMethod.class);
String returnType = ReturnType.getReturnTypeByMethod(handlerMethod, apiMethod);
// return param
document.setReturnList(ReturnHandler.handlerReturn(method, returnType));
// return json
document.setReturnJson(ReturnHandler.handlerReturnJson(method, returnType));
boolean commentInReturn = globalCommentInReturn;
boolean recordLevel = globalRecordLevel;
if (Tools.isNotNull(apiMethod)) {
document.setTitle(apiMethod.value());
document.setDesc(apiMethod.desc());
document.setDevelop(apiMethod.develop());
document.setIndex(apiMethod.index());
document.setCommentInReturnExampleWithLevel(apiMethod.commentInReturnExampleWithLevel());
boolean[] commentInReturnExample = apiMethod.commentInReturnExample();
if (commentInReturnExample.length > 0) {
commentInReturn = commentInReturnExample[0];
}
boolean[] returnRecordLevel = apiMethod.returnRecordLevel();
if (returnRecordLevel.length > 0) {
recordLevel = returnRecordLevel[0];
}
}
document.setCommentInReturnExample(commentInReturn);
document.setReturnRecordLevel(recordLevel);
String id = document.getId();
document.setExampleUrl(getExampleUrl(id));
// response
document.setResponseList(methodResponse(handlerMethod, commentInReturn, recordLevel));
documentMap.put(id, document);
// add DocumentUrl to DocumentModule
ApiGroup apiGroup = getAnnotation(handlerMethod, ApiGroup.class);
// ClassName(if className include Controller then remove)
String className = handlerMethod.getBeanType().getSimpleName();
String info = className;
String classSuffix = "controller";
String lowerClassName = className.toLowerCase();
if (lowerClassName.endsWith(classSuffix)) {
info = className.substring(0, lowerClassName.indexOf(classSuffix));
}
if (Tools.isNull(apiGroup)) {
addGroup(moduleMap, 0, info + ApiConst.HORIZON + className, document);
} else {
int index = apiGroup.index();
for (String group : apiGroup.value()) {
if (Tools.isNotNull(group)) {
String groupName = group.contains(ApiConst.HORIZON)
? group : (info + ApiConst.HORIZON + group);
addGroup(moduleMap, index, groupName, document);
}
}
}
}
}
}
}
Collection modules = moduleMap.values();
List moduleList = new LinkedList<>();
if (Tools.isNotEmpty(modules)) {
for (DocumentModule module : modules) {
Collections.sort(module.getUrlList());
moduleList.add(module);
}
Collections.sort(moduleList);
}
DocumentInfo documentInfo = new DocumentInfo()
.setTokenList(copyright.getGlobalTokens())
.setResponseList(globalResponse(copyright.getGlobalResponse(), globalCommentInReturn, globalRecordLevel))
.setEnumInfo(Tools.allEnumInfo())
.setModuleList(moduleList);
return new DocumentInfoAndUrlMap(documentInfo, documentMap);
}
private static Set getUrl(RequestMappingInfo requestMapping) {
Set urlSet = new LinkedHashSet<>();
/*
PathPatternsRequestCondition pathPatternsCondition = requestMapping.getPathPatternsCondition();
if (Tools.isNotBlank(pathPatternsCondition)) {
Set patterns = pathPatternsCondition.getPatterns();
if (Tools.isNotEmpty(patterns)) {
for (PathPattern pattern : patterns) {
urlSet.add(pattern.getPatternString());
}
}
}
*/
try {
Object condition = requestMapping.getClass().getMethod("getPathPatternsCondition").invoke(requestMapping);
if (Tools.isNotNull(condition)) {
// noinspection unchecked
Set patterns = (Set) condition.getClass().getMethod("getPatterns").invoke(condition);
if (Tools.isNotEmpty(patterns)) {
for (PathPattern pattern : patterns) {
urlSet.add(pattern.getPatternString());
}
}
}
} catch (Exception e) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("getPathPatternsCondition just support with spring-mvc 5.3", e);
}
}
if (urlSet.isEmpty()) {
PatternsRequestCondition condition = requestMapping.getPatternsCondition();
if (Tools.isNotNull(condition)) {
urlSet.addAll(condition.getPatterns());
}
}
return urlSet;
}
private static Set getMethod(RequestMappingInfo requestMapping) {
Set methodSet = new LinkedHashSet<>();
Set methodArray = requestMapping.getMethodsCondition().getMethods();
if (Tools.isNotEmpty(methodArray)) {
for (RequestMethod method : methodArray) {
methodSet.add(method.name());
}
}
return methodSet;
}
private static List globalResponse(List globalResponse,
boolean globalCommentInReturn,
boolean globalRecordLevel) {
if (Tools.isEmpty(globalResponse)) {
return Collections.emptyList();
}
for (DocumentResponse response : globalResponse) {
String type = ReturnType.getReturnTypeByResponse(response);
if (Tools.isNotNull(type)) {
String method = response.getCode() + ":" + response.getMsg();
String json = ReturnHandler.handlerReturnJson(method, type);
List returnList = ReturnHandler.handlerReturn(method, type);
response.setComment(DocumentUrl.commentJson(json, globalCommentInReturn, true, returnList));
response.setReturnList(DocumentUrl.returnList(globalCommentInReturn, globalRecordLevel, returnList));
}
}
return globalResponse;
}
private static List methodResponse(HandlerMethod handlerMethod,
boolean methodCommentInReturn,
boolean methodRecordLevel) {
List responseList = new LinkedList<>();
ApiResponses responses = getAnnotation(handlerMethod, ApiResponses.class);
if (Tools.isNotNull(responses)) {
for (ApiResponse apiResponse : responses.value()) {
DocumentResponse response = new DocumentResponse(apiResponse);
String type = ReturnType.getReturnTypeByAnnotation(Tools.first(apiResponse.type()));
if (Tools.isNotNull(type)) {
String method = handlerMethod.toString();
String json = ReturnHandler.handlerReturnJson(method, type);
List returnList = ReturnHandler.handlerReturn(method, type);
response.setComment(DocumentUrl.commentJson(json, methodCommentInReturn, true, returnList));
response.setReturnList(DocumentUrl.returnList(methodCommentInReturn, methodRecordLevel, returnList));
}
responseList.add(response);
}
}
return responseList;
}
private static boolean hasRequestBody(HandlerMethod handlerMethod) {
MethodParameter[] parameters = handlerMethod.getMethodParameters();
if (Tools.isNotEmpty(parameters) && parameters.length == 1) {
MethodParameter parameter = parameters[0];
RequestBody requestBody = parameter.getParameterAnnotation(RequestBody.class);
return Tools.isNotNull(requestBody);
} else {
return false;
}
}
private static boolean hasBasicParamRequestBody(HandlerMethod handlerMethod) {
MethodParameter[] parameters = handlerMethod.getMethodParameters();
if (Tools.isNotEmpty(parameters) && parameters.length == 1) {
MethodParameter parameter = parameters[0];
RequestBody requestBody = parameter.getParameterAnnotation(RequestBody.class);
return Tools.isNotNull(requestBody) && Tools.basicType(parameter.getParameterType());
} else {
return false;
}
}
private static boolean hasInnerParamRequestBody(HandlerMethod handlerMethod) {
MethodParameter[] parameters = handlerMethod.getMethodParameters();
if (Tools.isNotEmpty(parameters) && parameters.length == 1) {
MethodParameter parameter = parameters[0];
RequestBody requestBody = parameter.getParameterAnnotation(RequestBody.class);
// return Tools.isNotBlank(requestBody) &&
// (Tools.innerType(parameter.getParameterType()) || Tools.innerType(parameter.getParameter().getParameterizedType()));
return Tools.isNotNull(requestBody);
}
return false;
}
private static String getExampleUrl(String param) {
// return exampleUrl.replaceFirst("\\{.*?\\}", param);
String url = ApiConst.URL_PREFIX + ApiConst.URL_EXAMPLE;
return Requests.getDomain() + ApiConst.ID_URL_PATTERN.matcher(url).replaceFirst(param);
}
private static void addGroup(Map moduleMap, int index, String group, DocumentUrl url) {
DocumentModule module = moduleMap.get(group);
if (Tools.isNull(module)) {
module = new DocumentModule(group);
module.setIndex(index);
} else if (module.getIndex() > index) {
// if set multiple-module and different index, use the smaller
module.setIndex(index);
}
module.addUrl(url);
moduleMap.put(group, module);
}
private static boolean ignoreUrl(Set urlSet, Set methodSet, Set ignoreUrlSet) {
if (Tools.isNull(ignoreUrlSet)) {
ignoreUrlSet = Tools.sets();
}
for (String ignoreUrl : ignoreUrlSet) {
if (!ignoreUrl.startsWith("/")) {
ignoreUrl = "/" + ignoreUrl;
}
if (ignoreUrl.contains("*")) {
ignoreUrl = ignoreUrl.replace("*", "(.*)?");
String[] urlAndMethod = ignoreUrl.split("\\|");
if (urlAndMethod.length == 2) {
String tmpUrl = urlAndMethod[0];
String tmpMethod = urlAndMethod[1].toUpperCase();
if (methodSet.contains(tmpMethod)) {
for (String url : urlSet) {
if (url.matches(tmpUrl)) {
return true;
}
}
}
} else {
for (String url : urlSet) {
if (url.matches(ignoreUrl)) {
return true;
}
}
}
} else {
String[] urlAndMethod = ignoreUrl.split("\\|");
if (urlAndMethod.length == 2) {
String tmpUrl = urlAndMethod[0];
String tmpMethod = urlAndMethod[1].toUpperCase();
if (urlSet.contains(tmpUrl) && methodSet.contains(tmpMethod)) {
return true;
}
} else if (urlSet.contains(ignoreUrl)) {
return true;
}
}
}
return false;
}
private static boolean wasJsonApi(HandlerMethod handlerMethod) {
// @ResponseBody can be annotation on method and class
if (Tools.isNotNull(getAnnotation(handlerMethod, ResponseBody.class))) {
return true;
} else {
// @RestController just annotation on class
return Tools.isNotNull(getAnnotationByClass(handlerMethod, RestController.class));
}
}
private static T getAnnotationByClass(HandlerMethod handlerMethod, Class clazz) {
return AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), clazz);
}
private static T getAnnotation(HandlerMethod handlerMethod, Class clazz) {
T annotation = handlerMethod.getMethodAnnotation(clazz);
if (Tools.isNull(annotation)) {
annotation = getAnnotationByClass(handlerMethod, clazz);
}
return annotation;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy