diff --git a/.gitignore b/.gitignore index 7e1cc57..e45ddb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,39 @@ -# Compiled class file -*.class +HELP.md +.gradle +build/ +target/ -# Log file -*.log +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -/target/ -bin +### STS ### +.apt_generated .classpath +.factorypath .project .settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/README.md b/README.md index 612ff2f..ad8aa90 100644 --- a/README.md +++ b/README.md @@ -74,14 +74,14 @@ https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/APIJSOND ## 初始化 ## Initialization -#### 1.在你项目的主程序启动类 Application 的 static {} 代码块配置 APIJSONApplication.DEFAULT_APIJSON_CREATOR,至少重写 createSQLConfig 方法返回你自己继承 APIJSONSQLConfig 的子类 -#### 1.Configure APIJSONApplication.DEFAULT_APIJSON_CREATOR in static {} of your Application, at least override createSQLConfig method and return your SQLConfig extends APIJSONSQLConfig. +#### 1.在你项目的主程序启动类 Application 的 static {} 代码块配置 APIJSONApplication.DEFAULT_APIJSON_CREATOR,至少重写 createSQLConfig 方法返回你自己继承 APIJSONSQLConfig 的子类 +#### 1.Configure APIJSONApplication.DEFAULT_APIJSON_CREATOR in static {} of your Application, at least override createSQLConfig method and return your APIJSONSQLConfig extends APIJSONSQLConfig. ```java static { - APIJSONApplication.DEFAULT_APIJSON_CREATOR = new APIJSONCreator() { + APIJSONApplication.DEFAULT_APIJSON_CREATOR = new APIJSONCreator() { @Override - public SQLConfig createSQLConfig() { + public DemoSQLConfig createSQLConfig() { return new DemoSQLConfig(); } }; @@ -132,6 +132,14 @@ https://github.com/Tencent/APIJSON/issues/36

-#### 点右上角 ⭐Star 支持一下,谢谢 ^_^ +### 贡献者 +### Contributors +1 个腾讯工程师、1 京东工程师 等,感谢大家的贡献~
+1 Tencent engineer, 1 JD engineer, etc. Thank you all~
+https://github.com/APIJSON/apijson-framework/graphs/contributors + +
+ +#### 创作不易、坚持更难,点右上角 ⭐Star 支持一下,谢谢 ^_^ #### Please ⭐Star this project ^_^ https://github.com/APIJSON/apijson-framework diff --git a/libs/APIJSON-6.1.0.jar b/libs/APIJSON-6.1.0.jar deleted file mode 100644 index d0d51de..0000000 Binary files a/libs/APIJSON-6.1.0.jar and /dev/null differ diff --git a/libs/APIJSON-7.0.0.jar b/libs/APIJSON-7.0.0.jar new file mode 100644 index 0000000..9d39f67 Binary files /dev/null and b/libs/APIJSON-7.0.0.jar differ diff --git a/libs/apijson-column-2.0.0.jar b/libs/apijson-column-2.0.0.jar new file mode 100644 index 0000000..01a24d1 Binary files /dev/null and b/libs/apijson-column-2.0.0.jar differ diff --git a/libs/unitauto-jar-2.9.0.jar b/libs/unitauto-jar-3.0.5.jar similarity index 58% rename from libs/unitauto-jar-2.9.0.jar rename to libs/unitauto-jar-3.0.5.jar index b7315fe..346459c 100644 Binary files a/libs/unitauto-jar-2.9.0.jar and b/libs/unitauto-jar-3.0.5.jar differ diff --git a/libs/unitauto-java-2.9.0.jar b/libs/unitauto-java-2.9.0.jar deleted file mode 100644 index e52f0be..0000000 Binary files a/libs/unitauto-java-2.9.0.jar and /dev/null differ diff --git a/libs/unitauto-java-3.0.5.jar b/libs/unitauto-java-3.0.5.jar new file mode 100644 index 0000000..cc636d7 Binary files /dev/null and b/libs/unitauto-java-3.0.5.jar differ diff --git a/pom.xml b/pom.xml index 56d2e37..32eb7cc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ apijson.framework apijson-framework - 6.2.0 + 7.2.2 jar APIJSONFramework @@ -15,60 +15,46 @@ UTF-8 UTF-8 1.8 + UTF-8 + - javax.servlet - javax.servlet-api - 4.0.1 + jakarta.servlet + jakarta.servlet-api + 5.0.0 + provided + - com.alibaba - fastjson - 1.2.83 + javax.servlet + javax.servlet-api + 4.0.1 + provided com.github.Tencent APIJSON - 6.2.0 - - - com.github.APIJSON - apijson-column - 1.7.0 + 8.0.2 - mysql - mysql-connector-java - 8.0.33 + com.mysql + mysql-connector-j + 9.2.0 org.postgresql postgresql - 42.3.8 + 42.7.3 - - - - com.github.TommyLemon - unitauto-java - 2.9.0 - - - com.github.TommyLemon - unitauto-jar - 2.9.0 - - - @@ -76,7 +62,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.12.1 1.8 1.8 diff --git a/src/main/java/apijson/framework/APIJSONApplication.java b/src/main/java/apijson/framework/APIJSONApplication.java index 239c072..a6d08f0 100755 --- a/src/main/java/apijson/framework/APIJSONApplication.java +++ b/src/main/java/apijson/framework/APIJSONApplication.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ package apijson.framework; import java.rmi.ServerException; +import java.util.List; +import java.util.Map; import apijson.Log; import apijson.NotNull; @@ -30,11 +32,35 @@ public class APIJSONApplication { public static final String TAG = "APIJSONApplication"; @NotNull - public static APIJSONCreator DEFAULT_APIJSON_CREATOR; + public static APIJSONCreator, ? extends List> DEFAULT_APIJSON_CREATOR; static { DEFAULT_APIJSON_CREATOR = new APIJSONCreator<>(); } + @SuppressWarnings("unchecked") + public static , L extends List> APIJSONParser createParser() { + return (APIJSONParser) DEFAULT_APIJSON_CREATOR.createParser(); + } + @SuppressWarnings("unchecked") + public static , L extends List> APIJSONFunctionParser createFunctionParser() { + return (APIJSONFunctionParser) DEFAULT_APIJSON_CREATOR.createFunctionParser(); + } + + @SuppressWarnings("unchecked") + public static , L extends List> APIJSONVerifier createVerifier() { + return (APIJSONVerifier) DEFAULT_APIJSON_CREATOR.createVerifier(); + } + + @SuppressWarnings("unchecked") + public static , L extends List> APIJSONSQLConfig createSQLConfig() { + return (APIJSONSQLConfig) DEFAULT_APIJSON_CREATOR.createSQLConfig(); + } + + @SuppressWarnings("unchecked") + public static , L extends List> APIJSONSQLExecutor createSQLExecutor() { + return (APIJSONSQLExecutor) DEFAULT_APIJSON_CREATOR.createSQLExecutor(); + } + /**初始化,加载所有配置并校验 * @return @@ -56,7 +82,8 @@ public static void init(boolean shutdownWhenServerError) throws Exception { * @return * @throws Exception */ - public static void init(@NotNull APIJSONCreator creator) throws Exception { + public static , L extends List> void init( + @NotNull APIJSONCreator creator) throws Exception { init(true, creator); } /**初始化,加载所有配置并校验 @@ -65,16 +92,11 @@ public static void init(@NotNull APIJSONCreator creator) t * @return * @throws Exception */ - public static void init(boolean shutdownWhenServerError, @NotNull APIJSONCreator creator) throws Exception { + public static , L extends List> void init( + boolean shutdownWhenServerError, @NotNull APIJSONCreator creator) throws Exception { System.out.println("\n\n\n\n\n<<<<<<<<<<<<<<<<<<<<<<<<< APIJSON 开始启动 >>>>>>>>>>>>>>>>>>>>>>>>\n"); DEFAULT_APIJSON_CREATOR = creator; - // 统一用同一个 creator - APIJSONSQLConfig.APIJSON_CREATOR = creator; - APIJSONParser.APIJSON_CREATOR = creator; - APIJSONController.APIJSON_CREATOR = creator; - - if (APIJSONVerifier.ENABLE_VERIFY_ROLE) { System.out.println("\n\n\n开始初始化: Access 权限校验配置 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); try { @@ -138,6 +160,18 @@ public static void init(boolean shutdownWhenServerError, @Not System.out.println("\n完成测试: Request 请求参数校验 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); } + if (APIJSONVerifier.ENABLE_APIJSON_ROUTER) { + System.out.println("\n\n\n开始初始化: Document 请求映射配置 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + try { + APIJSONVerifier.initDocument(shutdownWhenServerError, creator); + } catch (Throwable e) { + e.printStackTrace(); + if (shutdownWhenServerError) { + onServerError("Document 请求映射配置 初始化失败!", shutdownWhenServerError); + } + } + System.out.println("\n完成初始化: Document 请求映射配置 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + } System.out.println("官方网站: http://apijson.cn"); @@ -156,8 +190,10 @@ protected static void onServerError(String msg, boolean shutdown) throws ServerE } } - public static void addScriptExecutor(String language, ScriptExecutor scriptExecutor) { + public static , L extends List> void addScriptExecutor(String language, ScriptExecutor scriptExecutor) { scriptExecutor.init(); AbstractFunctionParser.SCRIPT_EXECUTOR_MAP.put(language, scriptExecutor); } + + } diff --git a/src/main/java/apijson/framework/APIJSONConstant.java b/src/main/java/apijson/framework/APIJSONConstant.java index 105e81a..0dc9d9c 100644 --- a/src/main/java/apijson/framework/APIJSONConstant.java +++ b/src/main/java/apijson/framework/APIJSONConstant.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/main/java/apijson/framework/APIJSONController.java b/src/main/java/apijson/framework/APIJSONController.java index 0024d2a..5809bd5 100755 --- a/src/main/java/apijson/framework/APIJSONController.java +++ b/src/main/java/apijson/framework/APIJSONController.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,45 +14,18 @@ package apijson.framework; -import static apijson.RequestMethod.DELETE; -import static apijson.RequestMethod.GET; -import static apijson.RequestMethod.GETS; -import static apijson.RequestMethod.HEAD; -import static apijson.RequestMethod.HEADS; -import static apijson.RequestMethod.POST; -import static apijson.RequestMethod.PUT; -import static apijson.RequestMethod.CRUD; -import static apijson.framework.APIJSONConstant.ACCESS_; -import static apijson.framework.APIJSONConstant.METHODS; -import static apijson.framework.APIJSONConstant.DEFAULTS; -import static apijson.framework.APIJSONConstant.FORMAT; -import static apijson.framework.APIJSONConstant.FUNCTION_; -import static apijson.framework.APIJSONConstant.REQUEST_; -import static apijson.framework.APIJSONConstant.VERSION; -import static apijson.framework.APIJSONConstant.VISITOR_; -import static apijson.framework.APIJSONConstant.VISITOR_ID; - -import java.lang.reflect.Method; -import java.rmi.ServerException; -import java.util.Map; +import apijson.*; +import apijson.JSONRequest; +import apijson.orm.*; -import javax.servlet.AsyncContext; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpSession; -import com.alibaba.fastjson.JSONObject; +import java.rmi.ServerException; +import java.util.*; -import apijson.JSON; -import apijson.Log; -import apijson.NotNull; -import apijson.RequestMethod; -import apijson.StringUtil; -import apijson.orm.AbstractParser; -import apijson.orm.Parser; -import apijson.orm.Visitor; -import unitauto.MethodUtil; -import unitauto.MethodUtil.InterfaceProxy; +import static apijson.JSON.*; +import static apijson.RequestMethod.*; +import static apijson.framework.APIJSONConstant.*; /**APIJSON base controller,建议在子项目被 @RestController 注解的类继承它或通过它的实例调用相关方法 @@ -62,56 +35,200 @@ *
3.调试方便 - 建议使用 APIAuto-机器学习自动化接口管理工具(https://github.com/TommyLemon/APIAuto) * @author Lemon */ -public class APIJSONController { +public class APIJSONController, L extends List> { public static final String TAG = "APIJSONController"; - - @NotNull - public static APIJSONCreator APIJSON_CREATOR; - static { - APIJSON_CREATOR = new APIJSONCreator<>(); - } - + public String getRequestURL() { return null; } - public Parser newParser(HttpSession session, RequestMethod method) { - @SuppressWarnings("unchecked") - Parser parser = (Parser) APIJSON_CREATOR.createParser(); + public APIJSONParser newParser(HttpSession session, RequestMethod method) { + APIJSONParser parser = APIJSONApplication.createParser(); parser.setMethod(method); - if (parser instanceof APIJSONParser) { - ((APIJSONParser) parser).setSession(session); - } - // 可以更方便地通过日志排查错误 - if (parser instanceof AbstractParser) { - ((AbstractParser) parser).setRequestURL(getRequestURL()); - } + parser.setSession(session); + parser.setRequestURL(getRequestURL()); return parser; } + public static APIJSONParser, ? extends List> COMMON_PARSER = APIJSONApplication.createParser(); + + /**新建带状态内容的JSONObject + * @param code + * @param msg + * @return + */ + public static > M newResult(int code, String msg) { + return newResult(code, msg, null); + } + + /** + * 添加JSONObject的状态内容,一般用于错误提示结果 + * + * @param code + * @param msg + * @param warn + * @return + */ + public static > M newResult(int code, String msg, String warn) { + return newResult(code, msg, warn, false); + } + + /** + * 新建带状态内容的JSONObject + * + * @param code + * @param msg + * @param warn + * @param isRoot + * @return + */ + public static > M newResult(int code, String msg, String warn, boolean isRoot) { + return extendResult(null, code, msg, warn, isRoot); + } + + /** + * 添加JSONObject的状态内容,一般用于错误提示结果 + * + * @param object + * @param code + * @param msg + * @return + */ + public static > M extendResult(M object, int code, String msg, String warn, boolean isRoot) { + return (M) COMMON_PARSER.extendResult(JSON.createJSONObject(object), code, msg, warn, isRoot); + } + + + /** + * 添加请求成功的状态内容 + * + * @param object + * @return + */ + public M extendSuccessResult(M object) { + return extendSuccessResult(object, false); + } + + public M extendSuccessResult(M object, boolean isRoot) { + return extendSuccessResult(object, null, isRoot); + } + + /**添加请求成功的状态内容 + * @param object + * @param isRoot + * @return + */ + public static > M extendSuccessResult(M object, String warn, boolean isRoot) { + return extendResult(object, JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED, warn, isRoot); + } + + /**获取请求成功的状态内容 + * @return + */ + public static > M newSuccessResult() { + return newSuccessResult(null); + } + + /**获取请求成功的状态内容 + * @param warn + * @return + */ + public static > M newSuccessResult(String warn) { + return newSuccessResult(warn, false); + } + + /**获取请求成功的状态内容 + * @param warn + * @param isRoot + * @return + */ + public static > M newSuccessResult(String warn, boolean isRoot) { + return newResult(JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED, warn, isRoot); + } + + /**添加请求成功的状态内容 + * @param object + * @param e + * @return + */ + public static > M extendErrorResult(M object, Throwable e) { + return extendErrorResult(object, e, false); + } + /**添加请求成功的状态内容 + * @param object + * @param e + * @param isRoot + * @return + */ + public static > M extendErrorResult(M object, Throwable e, boolean isRoot) { + return extendErrorResult(object, e, null, null, isRoot); + } + /**添加请求成功的状态内容 + * @param object + * @return + */ + public static > M extendErrorResult(M object, Throwable e, RequestMethod requestMethod, String url, boolean isRoot) { + return (M) COMMON_PARSER.extendErrorResult(JSON.createJSONObject(object), e, requestMethod, url, isRoot); + } + + public static > M newErrorResult(Exception e) { + return newErrorResult(e, false); + } + public static > M newErrorResult(Exception e, boolean isRoot) { + return (M) COMMON_PARSER.newErrorResult(e, isRoot); + } + + public String parse(RequestMethod method, String request, HttpSession session) { + if (APIJSONVerifier.ENABLE_APIJSON_ROUTER && ! Log.DEBUG) { + return JSON.toJSONString( + newErrorResult( + new IllegalArgumentException("APIJSONVerifier.ENABLE_APIJSON_ROUTER = true 已启用 router," + + "Log.DEBUG = false 时不允许调用 /router/{method}/{tag} 外的万能通用接口!" + ) + ) + ); + } + return newParser(session, method).parse(request); } - + public String parseByTag(RequestMethod method, String tag, Map params, String request, HttpSession session) { - - JSONObject req = AbstractParser.wrapRequest(method, tag, JSON.parseObject(request), false); + if (APIJSONVerifier.ENABLE_APIJSON_ROUTER && ! Log.DEBUG) { + return JSON.toJSONString( + newErrorResult( + new IllegalArgumentException("APIJSONVerifier.ENABLE_APIJSON_ROUTER = true 已启用 router," + + "Log.DEBUG = false 时不允许调用 /router/{method}/{tag} 外的万能通用接口!" + ) + ) + ); + } + + APIJSONParser parser = newParser(session, method); + M req = parser.wrapRequest(method, tag, JSON.parseObject(request), false); if (req == null) { - req = new JSONObject(true); + req = JSON.createJSONObject(); } if (params != null && params.isEmpty() == false) { req.putAll(params); } - - return newParser(session, method).parse(req); + + return parser.parse(req); } //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + /**全能增删改查统一入口,这个一个方法可替代以下所有万能通用方法,一个接口通用增删改查 + * @param request + * @param session + * @return + */ + public String crudAll(String request, HttpSession session) { + return parse(CRUD, request, session); + } + /**增删改查统一入口,这个一个方法可替代以下 7 个方法,牺牲一点路由解析性能来提升一些开发效率 * @param method - * @param tag - * @param params * @param request * @param session * @return @@ -120,9 +237,11 @@ public String crud(String method, String request, HttpSession session) { if (METHODS.contains(method)) { return parse(RequestMethod.valueOf(method.toUpperCase()), request, session); } - - return APIJSONParser.newErrorResult(new IllegalArgumentException("URL 路径 /{method} 中 method 值 " + method - + " 错误!只允许 " + METHODS + " 中的一个!")).toJSONString(); + + return toJSONString(newErrorResult( + new IllegalArgumentException("URL 路径 /{method} 中 method 值 " + + method + " 错误!只允许 " + METHODS + " 中的一个!") + )); } /**获取 @@ -194,7 +313,7 @@ public String put(String request, HttpSession session) { public String delete(String request, HttpSession session) { return parse(DELETE, request, session); } - + /**支持全局事物、批量执行多条语句 * @param request 只用String,避免encode后未decode * @param session @@ -209,8 +328,8 @@ public String crud(String request, HttpSession session) { //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - - + + /**增删改查统一入口,这个一个方法可替代以下 7 个方法,牺牲一些路由解析性能来提升一点开发效率 * @param method * @param tag @@ -223,12 +342,14 @@ public String crudByTag(String method, String tag, Map params, S if (METHODS.contains(method)) { return parseByTag(RequestMethod.valueOf(method.toUpperCase()), tag, params, request, session); } - - return APIJSONParser.newErrorResult(new IllegalArgumentException("URL 路径 /{method}/{tag} 中 method 值 " + method - + " 错误!只允许 " + METHODS + " 中的一个!")).toJSONString(); + + return toJSONString(newErrorResult( + new IllegalArgumentException("URL 路径 /{method}/{tag} 中 method 值 " + + method + " 错误!只允许 " + METHODS + " 中的一个!") + )); } - + // /**获取列表 // * @param request 只用String,避免encode后未decode // * @param session @@ -236,7 +357,7 @@ public String crudByTag(String method, String tag, Map params, S // * @see {@link RequestMethod#GET} // */ // public String listByTag(String tag, String request, HttpSession session) { -// return parseByTag(GET, tag + JSONRequest.KEY_ARRAY, request, session); +// return parseByTag(GET, tag + apijson.JSONMap.KEY_ARRAY, request, session); // } /**获取 @@ -248,7 +369,7 @@ public String crudByTag(String method, String tag, Map params, S public String getByTag(String tag, Map params, String request, HttpSession session) { return parseByTag(GET, tag, params, request, session); } - + /**计数 * @param request 只用String,避免encode后未decode @@ -309,6 +430,227 @@ public String putByTag(String tag, Map params, String request, H public String deleteByTag(String tag, Map params, String request, HttpSession session) { return parseByTag(DELETE, tag, params, request, session); } + //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + /**增删改查统一的类 RESTful API 入口,牺牲一些路由解析性能来提升一点开发效率 + * compatCommonAPI = Log.DEBUG + * @param method + * @param tag + * @param params + * @param request + * @param session + * @return + */ + public String router(String method, String tag, Map params, String request, HttpSession session) { + return router(method, tag, params, request, session, Log.DEBUG); + } + /**增删改查统一的类 RESTful API 入口,牺牲一些路由解析性能来提升一点开发效率 + * @param method + * @param tag + * @param params + * @param request + * @param session + * @param compatCommonAPI 兼容万能通用 API,当没有映射 APIJSON 格式请求时,自动转到万能通用 API + * @return + */ + public String router(String method, String tag, Map params, String request, HttpSession session, boolean compatCommonAPI) { + if (! APIJSONVerifier.ENABLE_APIJSON_ROUTER) { + return JSON.toJSONString( + newErrorResult( + new IllegalArgumentException("未启用 router!请配置 APIJSONVerifier.ENABLE_APIJSON_ROUTER = true !" + ) + ) + ); + } + + RequestMethod requestMethod = null; + try { + requestMethod = RequestMethod.valueOf(method.toUpperCase()); + } catch (Throwable e) { + // 下方 METHODS.contains(method) 会抛异常 + } + Parser parser = newParser(session, requestMethod); + + if (METHODS.contains(method) == false) { + return JSON.toJSONString( + newErrorResult( + new IllegalArgumentException("URL 路径 /{method}/{tag} 中 method 值 " + + method + " 错误!只允许 " + METHODS + " 中的一个!" + ) + ) + ); + } + + String t = compatCommonAPI && tag != null && tag.endsWith("[]") ? tag.substring(0, tag.length() - 2) : tag; + if (StringUtil.isName(t) == false) { + return JSON.toJSONString( + newErrorResult( + new IllegalArgumentException("URL 路径 /" + method + "/{tag} 的 tag 中 " + + t + " 错误!tag 不能为空,且只允许变量命名格式!" + ) + ) + ); + } + + String versionStr = params == null ? null : params.remove(APIJSONConstant.VERSION); + Integer version; + try { + version = StringUtil.isEmpty(versionStr, false) ? null : Integer.valueOf(versionStr); + } + catch (Exception e) { + return JSON.toJSONString( + newErrorResult(new IllegalArgumentException("URL 路径 /" + method + "/" + + tag + "?version=value 中 value 值 " + versionStr + " 错误!必须符合整数格式!") + ) + ); + } + + if (version == null) { + version = 0; + } + + try { + // 从 Document 查这样的接口 + String cacheKey = AbstractVerifier.getCacheKeyForRequest(method, tag); + SortedMap> versionedMap = APIJSONVerifier.DOCUMENT_MAP.get(cacheKey); + + Map result = versionedMap == null ? null : versionedMap.get(version); + if (result == null) { // version <= 0 时使用最新,version > 0 时使用 > version 的最接近版本(最小版本) + Set>> set = versionedMap == null ? null : versionedMap.entrySet(); + + if (set != null && set.isEmpty() == false) { + Map.Entry> maxEntry = null; + + for (Map.Entry> entry : set) { + if (entry == null || entry.getKey() == null || entry.getValue() == null) { + continue; + } + + if (version == null || version <= 0 || version == entry.getKey()) { // 这里应该不会出现相等,因为上面 versionedMap.get(Integer.valueOf(version)) + maxEntry = entry; + break; + } + + if (entry.getKey() < version) { + break; + } + + maxEntry = entry; + } + + result = maxEntry == null ? null : maxEntry.getValue(); + } + + if (result != null) { // 加快下次查询,查到值的话组合情况其实是有限的,不属于恶意请求 + if (versionedMap == null) { + versionedMap = new TreeMap<>((o1, o2) -> { + return o2 == null ? -1 : o2.compareTo(o1); // 降序 + }); + } + + versionedMap.put(version, result); + APIJSONVerifier.DOCUMENT_MAP.put(cacheKey, versionedMap); + } + } + + @SuppressWarnings("unchecked") + APIJSONCreator creator = (APIJSONCreator) APIJSONApplication.DEFAULT_APIJSON_CREATOR; + if (result == null && Log.DEBUG && APIJSONVerifier.DOCUMENT_MAP.isEmpty()) { + + //获取指定的JSON结构 <<<<<<<<<<<<<< + SQLConfig config = creator.createSQLConfig().setMethod(GET).setTable(APIJSONConstant.DOCUMENT_); + config.setPrepared(false); + config.setColumn(Arrays.asList("request,apijson")); + + Map where = new HashMap(); + where.put("url", "/" + method + "/" + tag); + where.put("apijson{}", "length(apijson)>0"); + + if (version > 0) { + where.put(JSONRequest.KEY_VERSION + ">=", version); + } + config.setWhere(where); + config.setOrder(JSONRequest.KEY_VERSION + (version > 0 ? "+" : "-")); + config.setCount(1); + + //too many connections error: 不try-catch,可以让客户端看到是服务器内部异常 + result = creator.createSQLExecutor().execute(config, false); + + // version, method, tag 组合情况太多了,JDK 里又没有 LRUCache,所以要么启动时一次性缓存全部后面只用缓存,要么每次都查数据库 + // versionedMap.put(Integer.valueOf(version), result); + // DOCUMENT_MAP.put(cacheKey, versionedMap); + } + + String apijson = result == null ? null : getString(result, "apijson"); + if (StringUtil.isEmpty(apijson, true)) { // + if (compatCommonAPI) { + return crudByTag(method, tag, params, request, session); + } + + throw new IllegalArgumentException("URL 路径 /" + method + + "/" + tag + (versionStr == null ? "" : "?version=" + versionStr) + " 对应的接口不存在!"); + } + + M rawReq = JSON.parseObject(request); + if (rawReq == null) { + rawReq = JSON.createJSONObject(); + } + if (params != null && params.isEmpty() == false) { + rawReq.putAll(params); + } + + if (parser.isNeedVerifyContent()) { + Verifier verifier = parser.getVerifier(); + + //获取指定的JSON结构 <<<<<<<<<<<< + Map target = parser.getStructure("Request", method.toUpperCase(), tag, version); + if (target == null) { //empty表示随意操作 || object.isEmpty()) { + throw new UnsupportedOperationException("找不到 version: " + version + ", method: " + method.toUpperCase() + ", tag: " + tag + " 对应的 structure !" + + "非开放请求必须是后端 Request 表中校验规则允许的操作!如果需要则在 Request 表中新增配置!"); + } + + //M clone 浅拷贝没用,Structure.parse 会导致 structure 里面被清空,第二次从缓存里取到的就是 {} + verifier.verifyRequest(requestMethod, "", JSON.createJSONObject(target), rawReq, 0, null, null); + } + + M apijsonReq = JSON.parseObject(apijson); + if (apijsonReq == null) { + apijsonReq = JSON.createJSONObject(); + } + + Set> rawSet = rawReq.entrySet(); + if (rawSet != null && rawSet.isEmpty() == false) { + for (Map.Entry entry : rawSet) { + String key = entry == null ? null : entry.getKey(); + if (key == null) { // value 为 null 有效 + continue; + } + + String[] pathKeys = key.split("\\."); + //逐层到达child的直接容器JSONObject parent + int last = pathKeys.length - 1; + M parent = apijsonReq; + for (int i = 0; i < last; i++) {//一步一步到达指定位置 + M p = getJSONObject(parent, pathKeys[i]); + if (p == null) { + p = JSON.createJSONObject(); + parent.put(key, p); + } + parent = p; + } + + parent.put(pathKeys[last], entry.getValue()); + } + } + + // 没必要,已经是预设好的实际参数了,如果要 tag 就在 apijson 字段配置 apijsonReq.put(JSONRequest.KEY_TAG, tag); + + return parser.setNeedVerifyContent(false).parse(apijsonReq); + } + catch (Exception e) { + return JSON.toJSONString(newErrorResult(e)); + } + } //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -316,67 +658,66 @@ public String deleteByTag(String tag, Map params, String request /**重新加载配置 - * @param request * @return * @see *
-		{
-			"type": "ALL",  //重载对象,ALL, FUNCTION, REQUEST, ACCESS,非必须
-			"phone": "13000082001",
-			"verify": "1234567" //验证码,对应类型为 Verify.TYPE_RELOAD
-		}
+	{
+	"type": "ALL",  //重载对象,ALL, FUNCTION, REQUEST, ACCESS,非必须
+	"phone": "13000082001",
+	"verify": "1234567" //验证码,对应类型为 Verify.TYPE_RELOAD
+	}
 	 * 
*/ - public JSONObject reload(String type) { - JSONObject result = APIJSONParser.newSuccessResult(); + public M reload(String type) { + M result = newSuccessResult(); boolean reloadAll = StringUtil.isEmpty(type, true) || "ALL".equals(type); if (reloadAll || "ACCESS".equals(type)) { try { - if (reloadAll == false && APIJSONVerifier.ENABLE_VERIFY_ROLE == false) { - throw new UnsupportedOperationException("AbstractVerifier.ENABLE_VERIFY_ROLE == false 时不支持校验角色权限!" + - "如需支持则设置 AbstractVerifier.ENABLE_VERIFY_ROLE = true !"); - } - - if (APIJSONVerifier.ENABLE_VERIFY_ROLE) { - result.put(ACCESS_, APIJSONVerifier.initAccess()); - } - } catch (ServerException e) { + if (reloadAll == false && APIJSONVerifier.ENABLE_VERIFY_ROLE == false) { + throw new UnsupportedOperationException("AbstractVerifier.ENABLE_VERIFY_ROLE == false 时不支持校验角色权限!" + + "如需支持则设置 AbstractVerifier.ENABLE_VERIFY_ROLE = true !"); + } + + if (APIJSONVerifier.ENABLE_VERIFY_ROLE) { + result.put(ACCESS_, APIJSONVerifier.initAccess()); + } + } catch (ServerException e) { e.printStackTrace(); - result.put(ACCESS_, APIJSONParser.newErrorResult(e)); + result.put(ACCESS_, newErrorResult(e)); } } if (reloadAll || "FUNCTION".equals(type)) { try { - if (reloadAll == false && APIJSONFunctionParser.ENABLE_REMOTE_FUNCTION == false) { - throw new UnsupportedOperationException("AbstractFunctionParser.ENABLE_REMOTE_FUNCTION" + - " == false 时不支持远程函数!如需支持则设置 AbstractFunctionParser.ENABLE_REMOTE_FUNCTION = true !"); - } - - if (APIJSONFunctionParser.ENABLE_REMOTE_FUNCTION) { - result.put(FUNCTION_, APIJSONFunctionParser.init()); - } + if (reloadAll == false && APIJSONFunctionParser.ENABLE_REMOTE_FUNCTION == false) { + throw new UnsupportedOperationException("AbstractFunctionParser.ENABLE_REMOTE_FUNCTION" + + " == false 时不支持远程函数!如需支持则设置 AbstractFunctionParser.ENABLE_REMOTE_FUNCTION = true !"); + } + + if (APIJSONFunctionParser.ENABLE_REMOTE_FUNCTION) { + result.put(FUNCTION_, APIJSONFunctionParser.init()); + } } catch (ServerException e) { e.printStackTrace(); - result.put(FUNCTION_, APIJSONParser.newErrorResult(e)); + result.put(FUNCTION_, newErrorResult(e)); } } if (reloadAll || "REQUEST".equals(type)) { try { - if (reloadAll == false && APIJSONVerifier.ENABLE_VERIFY_CONTENT == false) { - throw new UnsupportedOperationException("AbstractVerifier.ENABLE_VERIFY_CONTENT == false 时不支持校验请求传参内容!" + - "如需支持则设置 AbstractVerifier.ENABLE_VERIFY_CONTENT = true !"); - } - - if (APIJSONVerifier.ENABLE_VERIFY_CONTENT) { - result.put(REQUEST_, APIJSONVerifier.initRequest()); - } + if (reloadAll == false && APIJSONVerifier.ENABLE_VERIFY_CONTENT == false) { + throw new UnsupportedOperationException("AbstractVerifier.ENABLE_VERIFY_CONTENT == false 时不支持校验请求传参内容!" + + "如需支持则设置 AbstractVerifier.ENABLE_VERIFY_CONTENT = true !"); + } + + if (APIJSONVerifier.ENABLE_VERIFY_CONTENT) { + result.put(REQUEST_, APIJSONVerifier.initRequest()); + } } catch (ServerException e) { e.printStackTrace(); - result.put(REQUEST_, APIJSONParser.newErrorResult(e)); + result.put(REQUEST_, newErrorResult(e)); } } @@ -385,14 +726,14 @@ public JSONObject reload(String type) { /**用户登录 - * @param session - * @param visitor - * @param version - * @param format - * @param defaults + * @param session + * @param visitor + * @param version + * @param format + * @param defaults * @return 返回类型设置为 Object 是为了子类重写时可以有返回值,避免因为冲突而另写一个有返回值的登录方法 */ - public Object login(@NotNull HttpSession session, Visitor visitor, Integer version, Boolean format, JSONObject defaults) { + public Object login(@NotNull HttpSession session, @NotNull Visitor visitor, Integer version, Boolean format, M defaults) { //登录状态保存至session session.setAttribute(VISITOR_ID, visitor.getId()); //用户id session.setAttribute(VISITOR_, visitor); //用户 @@ -415,62 +756,62 @@ public Object logout(@NotNull HttpSession session) { - public JSONObject listMethod(String request) { - if (Log.DEBUG == false) { - return APIJSONParser.newErrorResult(new IllegalAccessException("非 DEBUG 模式下不允许使用 UnitAuto 单元测试!")); - } - return MethodUtil.listMethod(request); - } - - public void invokeMethod(String request, HttpServletRequest servletRequest) { - AsyncContext asyncContext = servletRequest.startAsync(); - - final boolean[] called = new boolean[] { false }; - MethodUtil.Listener listener = new MethodUtil.Listener() { - - @Override - public void complete(JSONObject data, Method method, InterfaceProxy proxy, Object... extras) throws Exception { - - ServletResponse servletResponse = called[0] ? null : asyncContext.getResponse(); - if (servletResponse == null) { // || servletResponse.isCommitted()) { // isCommitted 在高并发时可能不准,导致写入多次 - Log.w(TAG, "invokeMethod listener.complete servletResponse == null || servletResponse.isCommitted() >> return;"); - return; - } - called[0] = true; - - servletResponse.setCharacterEncoding(servletRequest.getCharacterEncoding()); - servletResponse.setContentType(servletRequest.getContentType()); - servletResponse.getWriter().println(data); - asyncContext.complete(); - } - }; - - if (Log.DEBUG == false) { - try { - listener.complete(MethodUtil.JSON_CALLBACK.newErrorResult(new IllegalAccessException("非 DEBUG 模式下不允许使用 UnitAuto 单元测试!"))); - } - catch (Exception e1) { - e1.printStackTrace(); - asyncContext.complete(); - } - - return; - } - - - try { - MethodUtil.invokeMethod(request, null, listener); - } - catch (Exception e) { - Log.e(TAG, "invokeMethod try { JSONObject req = JSON.parseObject(request); ... } catch (Exception e) { \n" + e.getMessage()); - try { - listener.complete(MethodUtil.JSON_CALLBACK.newErrorResult(e)); - } - catch (Exception e1) { - e1.printStackTrace(); - asyncContext.complete(); - } - } - } +// public JSONMap listMethod(String request) { +// if (Log.DEBUG == false) { +// return APIJSONParser.newErrorResult(new IllegalAccessException("非 DEBUG 模式下不允许使用 UnitAuto 单元测试!")); +// } +// return MethodUtil.listMethod(request); +// } +// +// public void invokeMethod(String request, HttpServletRequest servletRequest) { +// AsyncContext asyncContext = servletRequest.startAsync(); +// +// final boolean[] called = new boolean[] { false }; +// MethodUtil.Listener listener = new MethodUtil.Listener() { +// +// @Override +// public void complete(JSONMap data, Method method, InterfaceProxy proxy, Object... extras) throws Exception { +// +// ServletResponse servletResponse = called[0] ? null : asyncContext.getResponse(); +// if (servletResponse == null) { // || servletResponse.isCommitted()) { // isCommitted 在高并发时可能不准,导致写入多次 +// Log.w(TAG, "invokeMethod listener.complete servletResponse == null || servletResponse.isCommitted() >> return;"); +// return; +// } +// called[0] = true; +// +// servletResponse.setCharacterEncoding(servletRequest.getCharacterEncoding()); +// servletResponse.setContentType(servletRequest.getContentType()); +// servletResponse.getWriter().println(data); +// asyncContext.complete(); +// } +// }; +// +// if (Log.DEBUG == false) { +// try { +// listener.complete(MethodUtil.JSON_CALLBACK.newErrorResult(new IllegalAccessException("非 DEBUG 模式下不允许使用 UnitAuto 单元测试!"))); +// } +// catch (Exception e1) { +// e1.printStackTrace(); +// asyncContext.complete(); +// } +// +// return; +// } +// +// +// try { +// MethodUtil.invokeMethod(request, null, listener); +// } +// catch (Exception e) { +// Log.e(TAG, "invokeMethod try { JSONMap req = JSON.parseObject(request); ... } catch (Exception e) { \n" + e.getMessage()); +// try { +// listener.complete(MethodUtil.JSON_CALLBACK.newErrorResult(e)); +// } +// catch (Exception e1) { +// e1.printStackTrace(); +// asyncContext.complete(); +// } +// } +// } } diff --git a/src/main/java/apijson/framework/APIJSONCreator.java b/src/main/java/apijson/framework/APIJSONCreator.java index 355a5ba..17e7ed7 100644 --- a/src/main/java/apijson/framework/APIJSONCreator.java +++ b/src/main/java/apijson/framework/APIJSONCreator.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,44 +14,43 @@ package apijson.framework; -import apijson.orm.FunctionParser; -import apijson.orm.Parser; import apijson.orm.ParserCreator; -import apijson.orm.SQLConfig; import apijson.orm.SQLCreator; -import apijson.orm.SQLExecutor; -import apijson.orm.Verifier; import apijson.orm.VerifierCreator; +import java.util.List; +import java.util.Map; + /**APIJSON相关创建器 * @author Lemon */ -public class APIJSONCreator implements ParserCreator, VerifierCreator, SQLCreator { +public class APIJSONCreator, L extends List> + implements ParserCreator, VerifierCreator, SQLCreator { @Override - public Parser createParser() { + public APIJSONParser createParser() { return new APIJSONParser<>(); } @Override - public FunctionParser createFunctionParser() { - return new APIJSONFunctionParser(); + public APIJSONFunctionParser createFunctionParser() { + return new APIJSONFunctionParser<>(); } @Override - public Verifier createVerifier() { + public APIJSONVerifier createVerifier() { return new APIJSONVerifier<>(); } @Override - public SQLConfig createSQLConfig() { - return new APIJSONSQLConfig(); + public APIJSONSQLConfig createSQLConfig() { + return new APIJSONSQLConfig<>(); } @Override - public SQLExecutor createSQLExecutor() { - return new APIJSONSQLExecutor(); + public APIJSONSQLExecutor createSQLExecutor() { + return new APIJSONSQLExecutor<>(); } } diff --git a/src/main/java/apijson/framework/APIJSONFunctionParser.java b/src/main/java/apijson/framework/APIJSONFunctionParser.java index 332c80f..b4781d1 100755 --- a/src/main/java/apijson/framework/APIJSONFunctionParser.java +++ b/src/main/java/apijson/framework/APIJSONFunctionParser.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,54 +14,33 @@ package apijson.framework; -import static apijson.RequestMethod.DELETE; -import static apijson.RequestMethod.GET; -import static apijson.RequestMethod.GETS; -import static apijson.RequestMethod.HEAD; -import static apijson.RequestMethod.HEADS; -import static apijson.RequestMethod.POST; -import static apijson.RequestMethod.PUT; -import static apijson.framework.APIJSONConstant.FUNCTION_; -import static apijson.framework.APIJSONConstant.SCRIPT_; - -import java.io.IOException; -import java.rmi.ServerException; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpSession; - -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; - -import apijson.JSON; -import apijson.JSONResponse; -import apijson.Log; -import apijson.NotNull; -import apijson.RequestMethod; -import apijson.StringUtil; +import apijson.*; import apijson.orm.AbstractFunctionParser; -import apijson.orm.JSONRequest; import apijson.orm.script.JavaScriptExecutor; import apijson.orm.script.ScriptExecutor; -import unitauto.MethodUtil; -import unitauto.MethodUtil.Argument; +import jakarta.servlet.http.HttpSession; +//import unitauto.MethodUtil; +//import unitauto.MethodUtil.Argument; + +import java.rmi.ServerException; +import java.util.*; + +import static apijson.JSON.*; +import static apijson.JSONRequest.KEY_COUNT; +import static apijson.RequestMethod.*; +import static apijson.framework.APIJSONConstant.FUNCTION_; +import static apijson.framework.APIJSONConstant.SCRIPT_; /**可远程调用的函数类 * @author Lemon */ -public class APIJSONFunctionParser extends AbstractFunctionParser { +public class APIJSONFunctionParser, L extends List> extends AbstractFunctionParser { public static final String TAG = "APIJSONFunctionParser"; - @NotNull - public static APIJSONCreator APIJSON_CREATOR; @NotNull public static final String[] ALL_METHODS; static { - APIJSON_CREATOR = new APIJSONCreator<>(); ALL_METHODS = new String[]{ GET.name(), HEAD.name(), GETS.name(), HEADS.name(), POST.name(), PUT.name(), DELETE.name() }; } @@ -72,184 +51,185 @@ public APIJSONFunctionParser() { public APIJSONFunctionParser(HttpSession session) { this(null, null, 0, null, session); } - public APIJSONFunctionParser(RequestMethod method, String tag, int version, JSONObject curObj, HttpSession session) { + public APIJSONFunctionParser(RequestMethod method, String tag, int version, M curObj, HttpSession session) { super(method, tag, version, curObj); setSession(session); } + public HttpSession getSession() { return session; } - public APIJSONFunctionParser setSession(HttpSession session) { + public APIJSONFunctionParser setSession(HttpSession session) { this.session = session; return this; } @Override - public APIJSONFunctionParser setMethod(RequestMethod method) { + public APIJSONFunctionParser setMethod(RequestMethod method) { super.setMethod(method); return this; } @Override - public APIJSONFunctionParser setTag(String tag) { + public APIJSONFunctionParser setTag(String tag) { super.setTag(tag); return this; } @Override - public APIJSONFunctionParser setVersion(int version) { + public APIJSONFunctionParser setVersion(int version) { super.setVersion(version); return this; } /**初始化,加载所有远程函数配置,并校验是否已在应用层代码实现 - * @return + * @return * @throws ServerException */ - public static JSONObject init() throws ServerException { + public static > M init() throws ServerException { return init(false); } /**初始化,加载所有远程函数配置,并校验是否已在应用层代码实现 - * @param shutdownWhenServerError - * @return + * @param shutdownWhenServerError + * @return * @throws ServerException */ - public static JSONObject init(boolean shutdownWhenServerError) throws ServerException { + public static > M init(boolean shutdownWhenServerError) throws ServerException { return init(shutdownWhenServerError, null); } /**初始化,加载所有远程函数配置,并校验是否已在应用层代码实现 - * @param creator - * @return + * @param creator + * @return * @throws ServerException */ - public static JSONObject init(APIJSONCreator creator) throws ServerException { + public static , L extends List> M init(APIJSONCreator creator) throws ServerException { return init(false, creator); } /**初始化,加载所有远程函数配置,并校验是否已在应用层代码实现 - * @param shutdownWhenServerError - * @param creator - * @return + * @param shutdownWhenServerError + * @param creator + * @return * @throws ServerException */ - public static JSONObject init(boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { + public static , L extends List> M init(boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { return init(shutdownWhenServerError, creator, null); } /**初始化,加载所有远程函数配置,并校验是否已在应用层代码实现 - * @param shutdownWhenServerError - * @param creator + * @param shutdownWhenServerError + * @param creator * @param table 表内自定义数据过滤条件 - * @return + * @return * @throws ServerException */ @SuppressWarnings("unchecked") - public static JSONObject init(boolean shutdownWhenServerError, APIJSONCreator creator, JSONObject table) throws ServerException { + public static , L extends List> M init(boolean shutdownWhenServerError + , APIJSONCreator creator, M table) throws ServerException { if (creator == null) { - creator = (APIJSONCreator) APIJSON_CREATOR; + creator = (APIJSONCreator) APIJSONApplication.DEFAULT_APIJSON_CREATOR; } - APIJSON_CREATOR = creator; - boolean isAll = table == null || table.isEmpty(); - //JSONObject function = isAll ? new JSONRequest() : table; + //JSONRequest function = isAll ? JSON.createJSONObject() : table; //if (Log.DEBUG == false) { // function.put(APIJSONConstant.KEY_DEBUG, 0); //} - // - //JSONRequest functionItem = new JSONRequest(); + // + //JSONRequest functionItem = JSON.createJSONObject(); //functionItem.put(FUNCTION_, function); - // - //JSONObject script = new JSONRequest(); // isAll ? new JSONRequest() : table; - //script.put("simple", 0); - //if (Log.DEBUG == false) { - // script.put(APIJSONConstant.KEY_DEBUG, 0); - //} - // 不能用这个来优化,因为可能配置了不校验远程函数是否存在 - //{ // name{}@ <<<<<<<<<<<<<<<<<<<<<<<<<<<<< - //JSONRequest nameInAt = new JSONRequest(); - //nameInAt.put("from", "Function"); - //{ // Function <<<<<<<<<<<<<<<<<<<<<<<<<<<<< - // JSONRequest fun = new JSONRequest(); - // fun.setColumn("name"); - // nameInAt.put("Function", fun); - //} // Function >>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - //script.put("name{}@", nameInAt); - //} // name{}@ >>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - //JSONRequest scriptItem = new JSONRequest(); - //scriptItem.put(SCRIPT_, script); - - JSONObject request = new JSONObject(); + // + //JSONRequest script = JSON.createJSONObject(); // isAll ? JSON.createJSONObject() : table; + //script.put("simple", 0); + //if (Log.DEBUG == false) { + // script.put(APIJSONConstant.KEY_DEBUG, 0); + //} + // 不能用这个来优化,因为可能配置了不校验远程函数是否存在 + //{ // name{}@ <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + //JSONRequest nameInAt = JSON.createJSONObject(); + //nameInAt.put("from", "Function"); + //{ // Function <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + // JSONRequest fun = JSON.createJSONObject(); + // fun.setColumn("name"); + // nameInAt.put("Function", fun); + //} // Function >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + //script.put("name{}@", nameInAt); + //} // name{}@ >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + //JSONRequest scriptItem = JSON.createJSONObject(); + //scriptItem.put(SCRIPT_, script); + + M request = JSON.createJSONObject(); //request.putAll(functionItem.toArray(0, 0, FUNCTION_)); //request.putAll(scriptItem.toArray(0, 0, SCRIPT_)); - // 可以用它,因为 Function 表必须存在,没有绕过校验的配置 // 不能用这个来优化,因为可能配置了不校验远程函数是否存在 - { // [] <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - JSONRequest item = new JSONRequest(); + // 可以用它,因为 Function 表必须存在,没有绕过校验的配置 // 不能用这个来优化,因为可能配置了不校验远程函数是否存在 + { // [] <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + M item = JSON.createJSONObject(); - { // Function <<<<<<<<<<<<<<<<<<<<<<<<<<<<< - JSONObject function = isAll ? new JSONRequest() : table; - if (Log.DEBUG == false) { - function.put(APIJSONConstant.KEY_DEBUG, 0); - } - item.put(FUNCTION_, function); - } // Function >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + { // Function <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + M function = isAll ? JSON.createJSONObject() : table; + if (! Log.DEBUG) { + function.put(APIJSONConstant.KEY_DEBUG, 0); + } + item.put(FUNCTION_, function); + } // Function >>>>>>>>>>>>>>>>>>>>>>>>>>>>> - if (ENABLE_SCRIPT_FUNCTION) { // Script <<<<<<<<<<<<<<<<<<<<<<<<<<<<< - JSONRequest script = new JSONRequest(); - script.put("name@", "/Function/name"); - script.put("simple", 0); - item.put(SCRIPT_, script); - } // Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + if (ENABLE_SCRIPT_FUNCTION) { // Script <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + M script = JSON.createJSONObject(); + script.put("name@", "/Function/name"); + script.put("simple", 0); + item.put(SCRIPT_, script); + } // Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>> - request.putAll(item.toArray(0, 0)); - } // [] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + item.put(KEY_COUNT, 0); + request.put("[]", item); + } // [] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - JSONObject response = creator.createParser().setMethod(GET).setNeedVerify(true).parseResponse(request); - if (JSONResponse.isSuccess(response) == false) { - onServerError("\n\n\n\n\n !!!! 查询远程函数异常 !!!\n" + response.getString(JSONResponse.KEY_MSG) + "\n\n\n\n\n", shutdownWhenServerError); + M response = creator.createParser().setMethod(GET).setNeedVerify(true).parseResponse(request); + if (! JSONResponse.isSuccess(response)) { + onServerError("\n\n\n\n\n !!!! 查询远程函数异常 !!!\n" + response.get(JSONResponse.KEY_MSG) + "\n\n\n\n\n", shutdownWhenServerError); } - + //初始化默认脚本引擎,避免增量 if (isAll || SCRIPT_EXECUTOR_MAP.get("js") == null) { - ScriptExecutor javaScriptExecutor = new JavaScriptExecutor(); + ScriptExecutor javaScriptExecutor = new JavaScriptExecutor<>(); javaScriptExecutor.init(); SCRIPT_EXECUTOR_MAP.put("js", javaScriptExecutor); SCRIPT_EXECUTOR_MAP.put("JavaScript", javaScriptExecutor); SCRIPT_EXECUTOR_MAP.put("javascript", javaScriptExecutor); } - Map scriptMap = new HashMap<>(); - JSONArray scriptList = response.getJSONArray("[]"); // response.getJSONArray(SCRIPT_ + "[]"); - if (scriptList != null && scriptList.isEmpty() == false) { - //if (isAll) { - // SCRIPT_MAP = new LinkedHashMap<>(); - //} - Map newMap = new LinkedHashMap<>(); - - for (int i = 0; i < scriptList.size(); i++) { - JSONObject item = scriptList.getJSONObject(i); - item = item == null ? null : item.getJSONObject(SCRIPT_); - if (item == null) { // 关联查不到很正常 - continue; - } - - String n = item.getString("name"); - if (StringUtil.isName(n) == false) { - onServerError("Script 表字段 name 的值 " + n + " 不合法!必须为合法的方法名字符串!", shutdownWhenServerError); - } - - String s = item.getString("script"); - if (StringUtil.isEmpty(s, true)) { - onServerError("Script 表字段 script 的值 " + s + " 不合法!不能为空!", shutdownWhenServerError); - } - newMap.put(n, item); - } - - scriptMap = newMap; - } - - JSONArray list = scriptList; // response.getJSONArray(FUNCTION_ + "[]"); + Map scriptMap = new HashMap<>(); + L scriptList = JSON.get(response, "[]"); // response.getJSONArray(SCRIPT_ + "[]"); + if (scriptList != null && ! scriptList.isEmpty()) { + //if (isAll) { + // SCRIPT_MAP = new LinkedHashMap<>(); + //} + Map newMap = new LinkedHashMap<>(); + + for (int i = 0; i < scriptList.size(); i++) { + M item = JSON.get(scriptList, i); + item = item == null ? null : JSON.get(item, SCRIPT_); + if (item == null) { // 关联查不到很正常 + continue; + } + + String n = getString(item, "name"); + if (! StringUtil.isName(n)) { + onServerError("Script 表字段 name 的值 " + n + " 不合法!必须为合法的方法名字符串!", shutdownWhenServerError); + } + + String s = getString(item, "script"); + if (StringUtil.isEmpty(s, true)) { + onServerError("Script 表字段 script 的值 " + s + " 不合法!不能为空!", shutdownWhenServerError); + } + newMap.put(n, item); + } + + scriptMap = newMap; + } + + L list = scriptList; // response.getJSONArray(FUNCTION_ + "[]"); int size = list == null ? 0 : list.size(); if (isAll && size <= 0) { Log.w(TAG, "init isAll && size <= 0,,没有可用的远程函数"); @@ -260,58 +240,72 @@ public static JSONObject init(boolean shutdownWhenServerError if (isAll) { // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! 如果要做成完全校验通过才更新 FUNCTION_MAP,但又不提供 忽略校验 参数,似乎无解 FUNCTION_MAP = new LinkedHashMap<>(); } - Map newMap = FUNCTION_MAP; // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! new LinkedHashMap<>(); + Map> newMap = FUNCTION_MAP; // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! new LinkedHashMap<>(); for (int i = 0; i < size; i++) { - JSONObject item = list.getJSONObject(i); - item = item == null ? null : item.getJSONObject(FUNCTION_); + M item = JSON.get(list, i); + item = item == null ? null : JSON.get(item, FUNCTION_); if (item == null) { continue; } - JSONObject demo = JSON.parseObject(item.getString("demo")); + M demo = JSON.parseObject(getString(item, "demo")); if (demo == null) { - onServerError("字段 demo 的值必须为合法且非 null 的 JSONObejct 字符串!", shutdownWhenServerError); - } - String name = item.getString("name"); - if (demo.containsKey("result()") == false) { - demo.put("result()", getFunctionCall(name, item.getString("arguments"))); + try { + onServerError("字段 demo 的值必须为合法且非 null 的 JSONObejct 字符串!", shutdownWhenServerError); + } catch (Exception e) { + throw new RuntimeException(e); + } } - // demo.put(JSONRequest.KEY_TAG, item.getString(JSONRequest.KEY_TAG)); - // demo.put(JSONRequest.KEY_VERSION, item.getInteger(JSONRequest.KEY_VERSION)); + String name = getString(item, "name"); + // demo.put(apijson.JSONRequest.KEY_TAG, getString(item, apijson.JSONRequest.KEY_TAG)); + // demo.put(apijson.JSONRequest.KEY_VERSION, item.getInteger(apijson.JSONRequest.KEY_VERSION)); //加载脚本 if (item.get("language") != null) { - String language = item.getString("language"); - if (SCRIPT_EXECUTOR_MAP.get(language) == null) { - onServerError("找不到脚本语言 " + language + " 对应的执行引擎!请先依赖相关库并在后端 APIJSONFunctionParser 中注册!", shutdownWhenServerError); + String language = getString(item, "language"); + // if (SCRIPT_EXECUTOR_MAP.get(language) == null) { + // onServerError("找不到脚本语言 " + language + " 对应的执行引擎!请先依赖相关库并在后端 APIJSONFunctionParser 中注册!", shutdownWhenServerError); + // } + //脚本语言执行 + if (SCRIPT_EXECUTOR_MAP.containsKey(language)){ + ScriptExecutor scriptExecutor = (ScriptExecutor) SCRIPT_EXECUTOR_MAP.get(language); + M script = scriptMap.get(name); + scriptExecutor.load(name, getString(script, "script")); } - ScriptExecutor scriptExecutor = SCRIPT_EXECUTOR_MAP.get(language); - scriptExecutor.load(name, scriptMap.get(name).getString("script")); } - newMap.put(name, item); // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! + newMap.put(name, item); // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! - String[] methods = StringUtil.split(item.getString("methods")); + String[] methods = StringUtil.split(getString(item, "methods")); if (methods == null || methods.length <= 0) { methods = ALL_METHODS; } - demo.put(JSONRequest.KEY_TAG, item.get(JSONRequest.KEY_TAG)); - demo.put(JSONRequest.KEY_VERSION, item.get(JSONRequest.KEY_VERSION)); + if (demo != null){ + if (! demo.containsKey("result()")) { + demo.put("result()", getFunctionCall(name, getString(item, "arguments"))); + } + demo.put(apijson.JSONRequest.KEY_TAG, item.get(apijson.JSONRequest.KEY_TAG)); + demo.put(apijson.JSONRequest.KEY_VERSION, item.get(apijson.JSONRequest.KEY_VERSION)); + } for (String method : methods) { - JSONObject r = APIJSON_CREATOR.createParser() - .setMethod(RequestMethod.valueOf(method)) + APIJSONParser parser = APIJSONApplication.createParser(); + M r = parser.setMethod(RequestMethod.valueOf(method)) .setNeedVerify(false) .parseResponse(demo); - if (JSONResponse.isSuccess(r) == false) { - onServerError(JSONResponse.getMsg(r), shutdownWhenServerError); + if (! JSONResponse.isSuccess(r)) { + try { + onServerError(JSONResponse.getMsg(r), shutdownWhenServerError); + } catch (Exception e) { + throw new RuntimeException(e); + } } } } - // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! + // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! // if (isAll) { // FUNCTION_MAP = newMap; // } @@ -327,7 +321,7 @@ protected static void onServerError(String msg, boolean shutdown) throws ServerE Log.e(TAG, "\n远程函数文档测试未通过!\n请新增 demo 里的函数,或修改 Function 表里的 demo 为已有的函数示例!\n保证前端看到的远程函数文档是正确的!!!\n\n原因:\n" + msg); if (shutdown) { - System.exit(1); + System.exit(1); } else { throw new ServerException(msg); } @@ -337,71 +331,83 @@ protected static void onServerError(String msg, boolean shutdown) throws ServerE public static void test() throws Exception { test(null); } - public static void test(APIJSONFunctionParser function) throws Exception { + public static , L extends List> void test( + APIJSONFunctionParser functionParser) throws Exception { int i0 = 1, i1 = -2; - JSONObject request = new JSONObject(); + M request = JSON.createJSONObject(); request.put("id", 10); request.put("i0", i0); request.put("i1", i1); - JSONArray arr = new JSONArray(); - arr.add(new JSONObject()); + L arr = JSON.createJSONArray(); + arr.add(JSON.createJSONObject()); request.put("arr", arr); - JSONArray array = new JSONArray(); - array.add(1);//new JSONObject()); - array.add(2);//new JSONObject()); - array.add(4);//new JSONObject()); - array.add(10);//new JSONObject()); + L array = JSON.createJSONArray(); + array.add(1);//JSON.createJSONObject()); + array.add(2);//JSON.createJSONObject()); + array.add(4);//JSON.createJSONObject()); + array.add(10);//JSON.createJSONObject()); request.put("array", array); request.put("position", 1); request.put("@position", 0); request.put("key", "key"); - JSONObject object = new JSONObject(); + M object = JSON.createJSONObject(); object.put("key", "success"); request.put("object", object); - if (function == null) { - function = new APIJSONFunctionParser(null, null, 1, null, null); + APIJSONParser parser = APIJSONApplication.createParser(); + parser.setRequest(request); + if (functionParser == null) { + functionParser = APIJSONApplication.createFunctionParser(); + functionParser.setParser(parser); + functionParser.setMethod(parser.getMethod()); + functionParser.setTag(parser.getTag()); + functionParser.setVersion(parser.getVersion()); + functionParser.setRequest(parser.getRequest()); + + //if (functionParser instanceof APIJSONFunctionParser) { + ((APIJSONFunctionParser) functionParser).setSession(parser.getSession()); + //} } + // functionParser.setKey(null); + // functionParser.setParentPath(null); + // functionParser.setCurrentName(null); + functionParser.setCurrentObject(request); + // 等数据库 Function 表加上 plus 配置再过两个以上迭代(应该是到 5.0)后再取消注释 // Log.i(TAG, "plus(1,-2) = " + function.invoke("plus(i0,i1)", request)); // AssertUtil.assertEqual(-1, function.invoke("plus(i0,i1)", request)); - Log.i(TAG, "count([1,2,4,10]) = " + function.invoke("countArray(array)", request)); - AssertUtil.assertEqual(4, function.invoke("countArray(array)", request)); + Log.i(TAG, "count([1,2,4,10]) = " + functionParser.invoke("countArray(array)", request)); + AssertUtil.assertEqual(4, functionParser.invoke("countArray(array)", request)); - Log.i(TAG, "isContain([1,2,4,10], 10) = " + function.invoke("isContain(array,id)", request)); - AssertUtil.assertEqual(true, function.invoke("isContain(array,id)", request)); + Log.i(TAG, "isContain([1,2,4,10], 10) = " + functionParser.invoke("isContain(array,id)", request)); + AssertUtil.assertEqual(true, functionParser.invoke("isContain(array,id)", request)); - Log.i(TAG, "getFromArray([1,2,4,10], 0) = " + function.invoke("getFromArray(array,@position)", request)); - AssertUtil.assertEqual(1, function.invoke("getFromArray(array,@position)", request)); + Log.i(TAG, "getFromArray([1,2,4,10], 0) = " + functionParser.invoke("getFromArray(array,@position)", request)); + AssertUtil.assertEqual(1, functionParser.invoke("getFromArray(array,@position)", request)); - Log.i(TAG, "getFromObject({key:\"success\"}, key) = " + function.invoke("getFromObject(object,key)", request)); - AssertUtil.assertEqual("success", function.invoke("getFromObject(object,key)", request)); + Log.i(TAG, "getFromObject({key:\"success\"}, key) = " + functionParser.invoke("getFromObject(object,key)", request)); + AssertUtil.assertEqual("success", functionParser.invoke("getFromObject(object,key)", request)); } - - - - - /**获取远程函数的demo,如果没有就自动补全 * @param curObj * @return - * @throws ServerException + * @throws ServerException */ - public JSONObject getFunctionDemo(@NotNull JSONObject curObj) { - JSONObject demo = JSON.parseObject(curObj.getString("demo")); + public M getFunctionDemo(@NotNull M curObj) { + M demo = JSON.parseObject(getString(curObj, "demo")); if (demo == null) { - demo = new JSONObject(); + demo = JSON.createJSONObject(); } - if (demo.containsKey("result()") == false) { - demo.put("result()", getFunctionCall(curObj.getString("name"), curObj.getString("arguments"))); + if (! demo.containsKey("result()")) { + demo.put("result()", getFunctionCall(getString(curObj, "name"), getString(curObj, "arguments"))); } return demo; } @@ -410,9 +416,9 @@ public JSONObject getFunctionDemo(@NotNull JSONObject curObj) { * @param curObj * @return */ - public String getFunctionDetail(@NotNull JSONObject curObj) { - return getFunctionCall(curObj.getString("name"), curObj.getString("arguments")) - + ": " + StringUtil.getTrimedString(curObj.getString("detail")); + public String getFunctionDetail(@NotNull M curObj) { + return getFunctionCall(getString(curObj, "name"), getString(curObj, "arguments")) + + ": " + StringUtil.trim(getString(curObj, "detail")); } /**获取函数调用代码 * @param name @@ -420,33 +426,33 @@ public String getFunctionDetail(@NotNull JSONObject curObj) { * @return */ private static String getFunctionCall(String name, String arguments) { - return name + "(" + StringUtil.getTrimedString(arguments) + ")"; + return name + "(" + StringUtil.trim(arguments) + ")"; } - public double plus(@NotNull JSONObject curObj, String i0, String i1) { - return curObj.getDoubleValue(i0) + curObj.getDoubleValue(i1); + public double plus(@NotNull M curObj, String i0, String i1) { + return getDoubleValue(curObj, i0) + getDoubleValue(curObj, i1); } - public double minus(@NotNull JSONObject curObj, String i0, String i1) { - return curObj.getDoubleValue(i0) - curObj.getDoubleValue(i1); + public double minus(@NotNull M curObj, String i0, String i1) { + return getDoubleValue(curObj, i0) - getDoubleValue(curObj, i1); } - public double multiply(@NotNull JSONObject curObj, String i0, String i1) { - return curObj.getDoubleValue(i0) * curObj.getDoubleValue(i1); + public double multiply(@NotNull M curObj, String i0, String i1) { + return getDoubleValue(curObj, i0) * getDoubleValue(curObj, i1); } - public double divide(@NotNull JSONObject curObj, String i0, String i1) { - return curObj.getDoubleValue(i0) / curObj.getDoubleValue(i1); + public double divide(@NotNull M curObj, String i0, String i1) { + return getDoubleValue(curObj, i0) / getDoubleValue(curObj, i1); } - public double plus(@NotNull JSONObject curObj, Number n0, Number n1) { + public double plus(@NotNull M curObj, Number n0, Number n1) { return n0.doubleValue() + n1.doubleValue(); } - public double minus(@NotNull JSONObject curObj, Number n0, Number n1) { + public double minus(@NotNull M curObj, Number n0, Number n1) { return n0.doubleValue() - n1.doubleValue(); } - public double multiply(@NotNull JSONObject curObj, Number n0, Number n1) { + public double multiply(@NotNull M curObj, Number n0, Number n1) { return n0.doubleValue() * n1.doubleValue(); } - public double divide(@NotNull JSONObject curObj, Number n0, Number n1) { + public double divide(@NotNull M curObj, Number n0, Number n1) { return n0.doubleValue() / n1.doubleValue(); } @@ -456,16 +462,16 @@ public double divide(@NotNull JSONObject curObj, Number n0, Number n1) { * @param array * @return */ - public boolean isArrayEmpty(@NotNull JSONObject curObj, String array) { - return BaseModel.isEmpty(curObj.getJSONArray(array)); + public boolean isArrayEmpty(@NotNull M curObj, String array) { + return BaseModel.isEmpty((Collection) getJSONArray(curObj, array)); } /**判断object是否为空 * @param curObj * @param object * @return */ - public boolean isObjectEmpty(@NotNull JSONObject curObj, String object) { - return BaseModel.isEmpty(curObj.getJSONObject(object)); + public boolean isObjectEmpty(@NotNull M curObj, String object) { + return BaseModel.isEmpty((Map) getJSONObject(curObj, object)); } //判断是否为空 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -476,13 +482,13 @@ public boolean isObjectEmpty(@NotNull JSONObject curObj, String object) { * @param value * @return */ - public boolean isContain(@NotNull JSONObject curObj, String array, String value) { + public boolean isContain(@NotNull M curObj, String array, String value) { //解决isContain((List) [82001,...], (Integer) 82001) == false及类似问题, list元素可能是从数据库查到的bigint类型的值 - // return BaseModel.isContain(curObj.getJSONArray(array), curObj.get(value)); + // return BaseModel.isContain(getJSONArray(curObj, array), curObj.get(value)); - //不用准确的的 curObj.getString(value).getClass() ,因为Long值转Integer崩溃,而且转成一种类型本身就和字符串对比效果一样了。 - List list = com.alibaba.fastjson.JSON.parseArray(curObj.getString(array), String.class); - return list != null && list.contains(curObj.getString(value)); + //不用准确的的 getString(curObj, value).getClass() ,因为Long值转Integer崩溃,而且转成一种类型本身就和字符串对比效果一样了。 + List list = JSON.parseArray(getString(curObj, array), String.class); + return list != null && list.contains(getString(curObj, value)); } /**判断object是否包含key * @param curObj @@ -490,8 +496,8 @@ public boolean isContain(@NotNull JSONObject curObj, String array, String value) * @param key * @return */ - public boolean isContainKey(@NotNull JSONObject curObj, String object, String key) { - return BaseModel.isContainKey(curObj.getJSONObject(object), curObj.getString(key)); + public boolean isContainKey(@NotNull M curObj, String object, String key) { + return BaseModel.isContainKey(getJSONObject(curObj, object), getString(curObj, key)); } /**判断object是否包含value * @param curObj @@ -499,8 +505,8 @@ public boolean isContainKey(@NotNull JSONObject curObj, String object, String ke * @param value * @return */ - public boolean isContainValue(@NotNull JSONObject curObj, String object, String value) { - return BaseModel.isContainValue(curObj.getJSONObject(object), curObj.get(value)); + public boolean isContainValue(@NotNull M curObj, String object, String value) { + return BaseModel.isContainValue(getJSONObject(curObj, object), curObj.get(value)); } //判断是否为包含 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -511,16 +517,16 @@ public boolean isContainValue(@NotNull JSONObject curObj, String object, String * @param array * @return */ - public int countArray(@NotNull JSONObject curObj, String array) { - return BaseModel.count(curObj.getJSONArray(array)); + public int countArray(@NotNull M curObj, String array) { + return BaseModel.count((Collection) getJSONArray(curObj, array)); } /**获取数量 * @param curObj * @param object * @return */ - public int countObject(@NotNull JSONObject curObj, String object) { - return BaseModel.count(curObj.getJSONObject(object)); + public int countObject(@NotNull M curObj, String object) { + return BaseModel.count((Map) getJSONObject(curObj, object)); } //获取集合长度 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -532,14 +538,14 @@ public int countObject(@NotNull JSONObject curObj, String object) { * @param position 支持直接传数字,例如 getFromArray(array,0) ;或者引用当前对象的值,例如 "@position": 0, "result()": "getFromArray(array,@position)" * @return */ - public Object getFromArray(@NotNull JSONObject curObj, String array, String position) { + public Object getFromArray(@NotNull M curObj, String array, String position) { int p; try { p = Integer.parseInt(position); } catch (Exception e) { - p = curObj.getIntValue(position); + p = getIntValue(curObj, position); } - return BaseModel.get(curObj.getJSONArray(array), p); + return BaseModel.get(getJSONArray(curObj, array), p); } /**获取 * @param curObj @@ -547,8 +553,8 @@ public Object getFromArray(@NotNull JSONObject curObj, String array, String posi * @param key * @return */ - public Object getFromObject(@NotNull JSONObject curObj, String object, String key) { - return BaseModel.get(curObj.getJSONObject(object), curObj.getString(key)); + public Object getFromObject(@NotNull M curObj, String object, String key) { + return BaseModel.get(getJSONObject(curObj, object), getString(curObj, key)); } //根据键获取值 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -558,14 +564,14 @@ public Object getFromObject(@NotNull JSONObject curObj, String object, String ke * @param position 支持直接传数字,例如 getFromArray(array,0) ;或者引用当前对象的值,例如 "@position": 0, "result()": "getFromArray(array,@position)" * @return */ - public Object removeIndex(@NotNull JSONObject curObj, String position) { + public Object removeIndex(@NotNull M curObj, String position) { int p; try { p = Integer.parseInt(position); } catch (Exception e) { - p = curObj.getIntValue(position); + p = getIntValue(curObj, position); } - curObj.remove(p); + curObj.remove(p); return null; } /**移除 @@ -573,7 +579,7 @@ public Object removeIndex(@NotNull JSONObject curObj, String position) { * @param key * @return */ - public Object removeKey(@NotNull JSONObject curObj, String key) { + public Object removeKey(@NotNull M curObj, String key) { curObj.remove(key); return null; } @@ -587,40 +593,40 @@ public Object removeKey(@NotNull JSONObject curObj, String key) { * @param value * @return */ - public boolean booleanValue(@NotNull JSONObject curObj, String value) { - return curObj.getBooleanValue(value); + public boolean booleanValue(@NotNull M curObj, String value) { + return getBooleanValue(curObj, value); } /**获取非空值 * @param curObj * @param value * @return */ - public int intValue(@NotNull JSONObject curObj, String value) { - return curObj.getIntValue(value); + public int intValue(@NotNull M curObj, String value) { + return getIntValue(curObj, value); } /**获取非空值 * @param curObj * @param value * @return */ - public long longValue(@NotNull JSONObject curObj, String value) { - return curObj.getLongValue(value); + public long longValue(@NotNull M curObj, String value) { + return getLongValue(curObj, value); } /**获取非空值 * @param curObj * @param value * @return */ - public float floatValue(@NotNull JSONObject curObj, String value) { - return curObj.getFloatValue(value); + public float floatValue(@NotNull M curObj, String value) { + return getFloatValue(curObj, value); } /**获取非空值 * @param curObj * @param value * @return */ - public double doubleValue(@NotNull JSONObject curObj, String value) { - return curObj.getDoubleValue(value); + public double doubleValue(@NotNull M curObj, String value) { + return getDoubleValue(curObj, value); } //获取非基本类型对应基本类型的非空值 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -630,181 +636,165 @@ public double doubleValue(@NotNull JSONObject curObj, String value) { * @param defaultValue * @return v == null ? curObj.get(defaultValue) : v */ - public Object getWithDefault(@NotNull JSONObject curObj, String value, String defaultValue) { - Object v = curObj.get(value); - return v == null ? curObj.get(defaultValue) : v; - } - - - - /**获取方法参数的定义 - * @param curObj - * @return - * @throws IOException - * @throws ClassNotFoundException - * @throws IllegalArgumentException - */ - public String getMethodArguments(@NotNull JSONObject curObj) throws IllegalArgumentException, ClassNotFoundException, IOException { - return getMethodArguments(curObj, "methodArgs"); - } - /**获取方法参数的定义 - * @param curObj - * @param methodArgsKey - * @return - * @throws IllegalArgumentException - * @throws ClassNotFoundException - * @throws IOException - */ - public String getMethodArguments(@NotNull JSONObject curObj, String methodArgsKey) throws IllegalArgumentException, ClassNotFoundException, IOException { - JSONObject obj = curObj.getJSONObject("request"); - String argsStr = obj == null ? null : obj.getString(methodArgsKey); - if (StringUtil.isEmpty(argsStr, true)) { - argsStr = curObj.getString(methodArgsKey); - } - List methodArgs = JSON.parseArray(removeComment(argsStr), Argument.class); - if (methodArgs == null || methodArgs.isEmpty()) { - return ""; - } - - // Class[] types = new Class[methodArgs.size()]; - // Object[] args = new Object[methodArgs.size()]; - // MethodUtil.initTypesAndValues(methodArgs, types, args, true); - - String s = ""; - // if (types != null) { - // String sn; - // for (int i = 0; i < types.length; i++) { - // sn = types[i] == null ? null : types[i].getSimpleName(); - // if (sn == null) { - // sn = Object.class.getSimpleName(); - // } - // - // if (i > 0) { - // s += ","; - // } - // - // if (MethodUtil.CLASS_MAP.containsKey(sn)) { - // s += sn; - // } - // else { - // s += types[i].getName(); - // } - // } - // } - - for (int i = 0; i < methodArgs.size(); i++) { - Argument arg = methodArgs.get(i); - - String sn = arg == null ? null : arg.getType(); - if (sn == null) { - sn = arg.getValue() == null ? Object.class.getSimpleName() : MethodUtil.trimType(arg.getValue().getClass()); - } - - if (i > 0) { - s += ","; - } - s += sn; - } - - return s; - } - - - /**改用 getMethodDefinition - */ - @Deprecated - public String getMethodDefination(@NotNull JSONObject curObj) throws IllegalArgumentException { - // curObj.put("arguments", removeComment(curObj.getString("methodArgs"))); - return getMethodDefination(curObj, "method", "arguments", "genericType", "genericExceptions", "Java"); - } - - /**获取方法的定义 - * @param curObj - * @return - * @throws IOException - * @throws ClassNotFoundException - * @throws IllegalArgumentException - */ - public String getMethodDefinition(@NotNull JSONObject curObj) throws IllegalArgumentException { - // curObj.put("arguments", removeComment(curObj.getString("methodArgs"))); - return getMethodDefinition(curObj, "method", "arguments", "genericType", "genericExceptions", "Java"); - } - /**改用 getMethodDefinition - */ - @Deprecated - public String getMethodDefination(@NotNull JSONObject curObj, String method, String arguments - , String type, String exceptions, String language) throws IllegalArgumentException { - return getMethodDefinition(curObj, method, arguments, type, exceptions, language); - } - /**获取方法的定义 - * @param curObj - * @param method - * @param arguments - * @param type - * @return method(argType0,argType1...): returnType - * @throws IOException - * @throws ClassNotFoundException - * @throws IllegalArgumentException - */ - public String getMethodDefinition(@NotNull JSONObject curObj, String method, String arguments - , String type, String exceptions, String language) throws IllegalArgumentException { - String n = curObj.getString(method); - if (StringUtil.isEmpty(n, true)) { - throw new NullPointerException("getMethodDefination StringUtil.isEmpty(methodArgs, true) !"); - } - String a = curObj.getString(arguments); - String t = curObj.getString(type); - String e = curObj.getString(exceptions); - - if (language == null) { - language = ""; - } - switch (language) { - case "TypeScript": - return n + "(" + (StringUtil.isEmpty(a, true) ? "" : a) + ")" + (StringUtil.isEmpty(t, true) ? "" : ": " + t) + (StringUtil.isEmpty(e, true) ? "" : " throws " + e); - case "Go": - return n + "(" + (StringUtil.isEmpty(a, true) ? "" : a ) + ")" + (StringUtil.isEmpty(t, true) ? "" : " " + t) + (StringUtil.isEmpty(e, true) ? "" : " throws " + e); - default: - //类型可能很长,Eclipse, Idea 代码提示都是类型放后面 return (StringUtil.isEmpty(t, true) ? "" : t + " ") + n + "(" + (StringUtil.isEmpty(a, true) ? "" : a) + ")"; - return n + "(" + (StringUtil.isEmpty(a, true) ? "" : a) + ")" + (StringUtil.isEmpty(t, true) ? "" : ": " + t) + (StringUtil.isEmpty(e, true) ? "" : " throws " + e); - } - } - - /** - * methodArgs 和 classArgs 都可以带注释 - */ - public String getMethodRequest(@NotNull JSONObject curObj) { - String req = curObj.getString("request"); - if (StringUtil.isEmpty(req, true) == false) { - return req; - } - - req = "{"; - Boolean isStatic = curObj.getBoolean("static"); - String methodArgs = curObj.getString("methodArgs"); - String classArgs = curObj.getString("classArgs"); - - boolean comma = false; - if (isStatic != null && isStatic) { - req += "\n \"static\": " + true; - comma = true; - } - if (StringUtil.isEmpty(methodArgs, true) == false) { - req += (comma ? "," : "") + "\n \"methodArgs\": " + methodArgs; - comma = true; - } - if (StringUtil.isEmpty(classArgs, true) == false) { - req += (comma ? "," : "") + "\n \"classArgs\": " + classArgs; - } - req += "\n}"; - return req; - } - - // public static JSONObject removeComment(String json) { - // return JSON.parseObject(removeComment(json)); + public Object getWithDefault(@NotNull M curObj, String value, String defaultValue) { + Object v = curObj.get(value); + return v == null ? curObj.get(defaultValue) : v; + } + + // FIXME UnitAuto 去除 fastjson 后恢复 + ///**获取方法参数的定义 + // * @param curObj + // * @return + // * @throws IOException + // * @throws ClassNotFoundException + // * @throws IllegalArgumentException + // */ + //public String getMethodArguments(@NotNull M curObj) throws IllegalArgumentException, ClassNotFoundException, IOException { + // return getMethodArguments(curObj, "methodArgs"); + //} + ///**获取方法参数的定义 + // * @param curObj + // * @param methodArgsKey + // * @return + // * @throws IllegalArgumentException + // * @throws ClassNotFoundException + // * @throws IOException + // */ + //public String getMethodArguments(@NotNull M curObj, String methodArgsKey) throws IllegalArgumentException, ClassNotFoundException, IOException { + // M obj = getJSONObject(curObj, "request"); + // String argsStr = obj == null ? null : getString(obj, methodArgsKey); + // if (StringUtil.isEmpty(argsStr, true)) { + // argsStr = getString(curObj, methodArgsKey); // } - public static String removeComment(String json) { - return json == null ? null: json.replaceAll("(//.*)|(/\\*[\\s\\S]*?\\*/)", ""); - } - -} \ No newline at end of file + // List methodArgs = JSON.parseArray(removeComment(argsStr), Argument.class); + // if (methodArgs == null || methodArgs.isEmpty()) { + // return ""; + // } + // + // // Class[] types = new Class[methodArgs.size()]; + // // Object[] args = new Object[methodArgs.size()]; + // // MethodUtil.initTypesAndValues(methodArgs, types, args, true); + // + // String s = ""; + // // if (types != null) { + // // String sn; + // // for (int i = 0; i < types.length; i++) { + // // sn = types[i] == null ? null : types[i].getSimpleName(); + // // if (sn == null) { + // // sn = Object.class.getSimpleName(); + // // } + // // + // // if (i > 0) { + // // s += ","; + // // } + // // + // // if (MethodUtil.CLASS_MAP.containsKey(sn)) { + // // s += sn; + // // } + // // else { + // // s += types[i].getName(); + // // } + // // } + // // } + // + // for (int i = 0; i < methodArgs.size(); i++) { + // Argument arg = methodArgs.get(i); + // + // String sn = arg == null ? null : arg.getType(); + // if (sn == null) { + // sn = arg.getValue() == null ? Object.class.getSimpleName() : MethodUtil.trimType(arg.getValue().getClass()); + // } + // + // if (i > 0) { + // s += ","; + // } + // s += sn; + // } + // + // return s; + //} + // + // + ///**获取方法的定义 + // * @param curObj + // * @return + // * @throws IOException + // * @throws ClassNotFoundException + // * @throws IllegalArgumentException + // */ + //public String getMethodDefinition(@NotNull M curObj) throws IllegalArgumentException { + // // curObj.put("arguments", removeComment(getString(curObj, "methodArgs"))); + // return getMethodDefinition(curObj, "method", "arguments", "genericType", "genericExceptions", "Java"); + //} + ///**获取方法的定义 + // * @param curObj + // * @param method + // * @param arguments + // * @param type + // * @return method(argType0,argType1...): returnType + // * @throws IOException + // * @throws ClassNotFoundException + // * @throws IllegalArgumentException + // */ + //public String getMethodDefinition(@NotNull M curObj, String method, String arguments + // , String type, String exceptions, String language) throws IllegalArgumentException { + // String n = getString(curObj, method); + // if (StringUtil.isEmpty(n, true)) { + // throw new NullPointerException("getMethodDefination StringUtil.isEmpty(methodArgs, true) !"); + // } + // String a = getString(curObj, arguments); + // String t = getString(curObj, type); + // String e = getString(curObj, exceptions); + // + // if (language == null) { + // language = ""; + // } + // switch (language) { + // case "TypeScript": + // return n + "(" + (StringUtil.isEmpty(a, true) ? "" : a) + ")" + (StringUtil.isEmpty(t, true) ? "" : ": " + t) + (StringUtil.isEmpty(e, true) ? "" : " throws " + e); + // case "Go": + // return n + "(" + (StringUtil.isEmpty(a, true) ? "" : a ) + ")" + (StringUtil.isEmpty(t, true) ? "" : " " + t) + (StringUtil.isEmpty(e, true) ? "" : " throws " + e); + // default: + // //类型可能很长,Eclipse, Idea 代码提示都是类型放后面 return (StringUtil.isEmpty(t, true) ? "" : t + " ") + n + "(" + (StringUtil.isEmpty(a, true) ? "" : a) + ")"; + // return n + "(" + (StringUtil.isEmpty(a, true) ? "" : a) + ")" + (StringUtil.isEmpty(t, true) ? "" : ": " + t) + (StringUtil.isEmpty(e, true) ? "" : " throws " + e); + // } + //} + // + ///** + // * methodArgs 和 classArgs 都可以带注释 + // */ + //public String getMethodRequest(@NotNull M curObj) { + // String req = getString(curObj, "request"); + // if (StringUtil.isEmpty(req, true) == false) { + // return req; + // } + // + // req = "{"; + // Boolean isStatic = getBoolean(curObj, "static"); + // String methodArgs = getString(curObj, "methodArgs"); + // String classArgs = getString(curObj, "classArgs"); + // + // boolean comma = false; + // if (isStatic != null && isStatic) { + // req += "\n \"static\": " + true; + // comma = true; + // } + // if (! StringUtil.isEmpty(methodArgs, true)) { + // req += (comma ? "," : "") + "\n \"methodArgs\": " + methodArgs; + // comma = true; + // } + // if (! StringUtil.isEmpty(classArgs, true)) { + // req += (comma ? "," : "") + "\n \"classArgs\": " + classArgs; + // } + // req += "\n}"; + // return req; + //} + // + //// public static JSONRequest removeComment(String json) { + //// return JSON.parseObject(removeComment(json)); + //// } + //public static String removeComment(String json) { + // return json == null ? null: json.replaceAll("(//.*)|(/\\*[\\s\\S]*?\\*/)", ""); + //} + +} diff --git a/src/main/java/apijson/framework/APIJSONObjectParser.java b/src/main/java/apijson/framework/APIJSONObjectParser.java index da2b588..c1e2e4c 100755 --- a/src/main/java/apijson/framework/APIJSONObjectParser.java +++ b/src/main/java/apijson/framework/APIJSONObjectParser.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,23 +15,20 @@ package apijson.framework; import java.util.List; +import java.util.Map; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpSession; -import com.alibaba.fastjson.JSONObject; +import apijson.orm.*; import apijson.NotNull; import apijson.RequestMethod; -import apijson.orm.AbstractObjectParser; -import apijson.orm.AbstractParser; -import apijson.orm.Join; -import apijson.orm.SQLConfig; /**简化Parser,getObject和getArray(getArrayConfig)都能用 * @author Lemon */ -public class APIJSONObjectParser extends AbstractObjectParser { +public class APIJSONObjectParser, L extends List> extends AbstractObjectParser { public static final String TAG = "APIJSONObjectParser"; /**for single object @@ -44,28 +41,28 @@ public class APIJSONObjectParser extends AbstractObjectParser { * @param isArrayMainTable * @throws Exception */ - public APIJSONObjectParser(HttpSession session, @NotNull JSONObject request, String parentPath, SQLConfig arrayConfig + public APIJSONObjectParser(HttpSession session, @NotNull M request, String parentPath, SQLConfig arrayConfig , boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception { super(request, parentPath, arrayConfig, isSubquery, isTable, isArrayMainTable); } @Override - public APIJSONObjectParser setMethod(RequestMethod method) { + public APIJSONObjectParser setMethod(RequestMethod method) { super.setMethod(method); return this; } @Override - public APIJSONObjectParser setParser(AbstractParser parser) { + public APIJSONObjectParser setParser(Parser parser) { super.setParser(parser); return this; } @Override - public SQLConfig newSQLConfig(RequestMethod method, String table, String alias, JSONObject request, List joinList, boolean isProcedure) throws Exception { + public SQLConfig newSQLConfig(RequestMethod method, String table, String alias, M request + , List> joinList, boolean isProcedure) throws Exception { return APIJSONSQLConfig.newSQLConfig(method, table, alias, request, joinList, isProcedure); } - } diff --git a/src/main/java/apijson/framework/APIJSONParser.java b/src/main/java/apijson/framework/APIJSONParser.java index 62d30b2..dfc76cd 100755 --- a/src/main/java/apijson/framework/APIJSONParser.java +++ b/src/main/java/apijson/framework/APIJSONParser.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,35 +18,25 @@ import static apijson.framework.APIJSONConstant.FORMAT; import static apijson.framework.APIJSONConstant.VERSION; +import java.util.List; import java.util.Map; import java.util.Set; -import javax.servlet.http.HttpSession; - -import com.alibaba.fastjson.JSONObject; +import jakarta.servlet.http.HttpSession; import apijson.NotNull; import apijson.RequestMethod; import apijson.orm.AbstractParser; import apijson.orm.FunctionParser; -import apijson.orm.Parser; import apijson.orm.SQLConfig; -import apijson.orm.SQLExecutor; -import apijson.orm.Verifier; /**请求解析器 * @author Lemon */ -public class APIJSONParser extends AbstractParser { +public class APIJSONParser, L extends List> extends AbstractParser { public static final String TAG = "APIJSONParser"; - @NotNull - public static APIJSONCreator APIJSON_CREATOR; - static { - APIJSON_CREATOR = new APIJSONCreator<>(); - } - public APIJSONParser() { super(); @@ -62,7 +52,7 @@ public APIJSONParser(RequestMethod method, boolean needVerify) { public HttpSession getSession() { return session; } - public APIJSONParser setSession(HttpSession session) { + public APIJSONParser setSession(HttpSession session) { this.session = session; setVisitor(APIJSONVerifier.getVisitor(session)); return this; @@ -70,39 +60,66 @@ public APIJSONParser setSession(HttpSession session) { @SuppressWarnings("unchecked") @Override - public Parser createParser() { - return (Parser) APIJSON_CREATOR.createParser(); + public APIJSONParser createParser() { + return APIJSONApplication.createParser(); } @Override - public FunctionParser createFunctionParser() { - return APIJSON_CREATOR.createFunctionParser(); + public APIJSONFunctionParser createFunctionParser() { + return APIJSONApplication.createFunctionParser(); } @SuppressWarnings("unchecked") @Override - public Verifier createVerifier() { - return (Verifier) APIJSON_CREATOR.createVerifier(); + public APIJSONVerifier createVerifier() { + return APIJSONApplication.createVerifier(); } @Override - public SQLConfig createSQLConfig() { - return APIJSON_CREATOR.createSQLConfig(); + public APIJSONSQLConfig createSQLConfig() { + return APIJSONApplication.createSQLConfig(); + } + @Override + public APIJSONSQLExecutor createSQLExecutor() { + return APIJSONApplication.createSQLExecutor(); + } + + @Override + public APIJSONParser setNeedVerify(boolean needVerify) { + super.setNeedVerify(needVerify); + return this; } + + @Override + public APIJSONParser setNeedVerifyLogin(boolean needVerifyLogin) { + super.setNeedVerifyLogin(needVerifyLogin); + return this; + } + @Override - public SQLExecutor createSQLExecutor() { - return APIJSON_CREATOR.createSQLExecutor(); + public APIJSONParser setNeedVerifyRole(boolean needVerifyRole) { + super.setNeedVerifyRole(needVerifyRole); + return this; } + @Override + public APIJSONParser setNeedVerifyContent(boolean needVerifyContent) { + super.setNeedVerifyContent(needVerifyContent); + return this; + } @Override - public JSONObject parseResponse(JSONObject request) { + public M parseResponse(M request) { //补充format if (session != null && request != null) { if (request.get(FORMAT) == null) { request.put(FORMAT, session.getAttribute(FORMAT)); } + if (request.get(VERSION) == null) { + request.put(VERSION, session.getAttribute(VERSION)); + } + if (request.get(DEFAULTS) == null) { - JSONObject defaults = (JSONObject) session.getAttribute(DEFAULTS); + M defaults = (M) session.getAttribute(DEFAULTS); Set> set = defaults == null ? null : defaults.entrySet(); if (set != null) { @@ -114,15 +131,16 @@ public JSONObject parseResponse(JSONObject request) { } } } + return super.parseResponse(request); } - private FunctionParser functionParser; - public FunctionParser getFunctionParser() { + private FunctionParser functionParser; + public FunctionParser getFunctionParser() { return functionParser; } @Override - public Object onFunctionParse(String key, String function, String parentPath, String currentName, JSONObject currentObject, boolean containRaw) throws Exception { + public Object onFunctionParse(String key, String function, String parentPath, String currentName, M currentObject, boolean containRaw) throws Exception { if (functionParser == null) { functionParser = createFunctionParser(); functionParser.setParser(this); @@ -131,8 +149,8 @@ public Object onFunctionParse(String key, String function, String parentPath, St functionParser.setVersion(getVersion()); functionParser.setRequest(requestObject); - if (functionParser instanceof APIJSONFunctionParser) { - ((APIJSONFunctionParser) functionParser).setSession(getSession()); + if (functionParser instanceof APIJSONFunctionParser) { + ((APIJSONFunctionParser) functionParser).setSession(getSession()); } } functionParser.setKey(key); @@ -145,13 +163,13 @@ public Object onFunctionParse(String key, String function, String parentPath, St @Override - public APIJSONObjectParser createObjectParser(JSONObject request, String parentPath, SQLConfig arrayConfig + public APIJSONObjectParser createObjectParser(@NotNull M request, String parentPath, SQLConfig arrayConfig , boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception { - return new APIJSONObjectParser(getSession(), request, parentPath, arrayConfig, isSubquery, isTable, isArrayMainTable) { + return new APIJSONObjectParser(getSession(), request, parentPath, arrayConfig, isSubquery, isTable, isArrayMainTable) { // @Override - // protected APIJSONSQLConfig newQueryConfig() { + // protected APIJSONSQLConfig newQueryConfig() { // if (itemConfig != null) { // return itemConfig; // } @@ -169,17 +187,4 @@ public APIJSONObjectParser createObjectParser(JSONObject request, String parentP }.setMethod(getMethod()).setParser(this); } - - - @Override - public void onVerifyContent() throws Exception { - //补充全局缺省版本号 //可能在默认为1的前提下这个请求version就需要为0 requestObject.getIntValue(VERSION) <= 0) { - HttpSession session = getSession(); - if (session != null && requestObject.get(VERSION) == null) { - requestObject.put(VERSION, session.getAttribute(VERSION)); - } - super.onVerifyContent(); - } - - } diff --git a/src/main/java/apijson/framework/APIJSONSQLConfig.java b/src/main/java/apijson/framework/APIJSONSQLConfig.java index e6cf6b0..af750db 100755 --- a/src/main/java/apijson/framework/APIJSONSQLConfig.java +++ b/src/main/java/apijson/framework/APIJSONSQLConfig.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,12 +19,13 @@ import static apijson.framework.APIJSONConstant.USER_; import static apijson.framework.APIJSONConstant.USER_ID; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import apijson.column.ColumnUtil; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.annotation.JSONField; +import apijson.JSONList; +import apijson.JSONMap; +//import apijson.column.ColumnUtil; import apijson.RequestMethod; import apijson.orm.AbstractSQLConfig; @@ -36,14 +37,13 @@ * TiDB 用法和 MySQL 一致 * @author Lemon */ -public class APIJSONSQLConfig extends AbstractSQLConfig { +public class APIJSONSQLConfig, L extends List> extends AbstractSQLConfig { public static final String TAG = "APIJSONSQLConfig"; public static boolean ENABLE_COLUMN_CONFIG = false; - public static Callback SIMPLE_CALLBACK; - public static APIJSONCreator APIJSON_CREATOR; - + public static Callback, ? extends List> SIMPLE_CALLBACK; + static { DEFAULT_DATABASE = DATABASE_MYSQL; //TODO 默认数据库类型,改成你自己的 DEFAULT_SCHEMA = "sys"; //TODO 默认模式名,改成你自己的,默认情况是 MySQL: sys, PostgreSQL: public, SQL Server: dbo, Oracle: @@ -54,13 +54,12 @@ public class APIJSONSQLConfig extends AbstractSQLConfig { // TABLE_KEY_MAP.put(User.class.getSimpleName(), "apijson_user"); // TABLE_KEY_MAP.put(Privacy.class.getSimpleName(), "apijson_privacy"); - APIJSON_CREATOR = new APIJSONCreator<>(); - - SIMPLE_CALLBACK = new SimpleCallback() { + SIMPLE_CALLBACK = new SimpleCallback, List>() { @Override - public SQLConfig getSQLConfig(RequestMethod method, String database, String schema,String datasource, String table) { - SQLConfig config = APIJSON_CREATOR.createSQLConfig(); + public SQLConfig, List> getSQLConfig( + RequestMethod method, String database, String schema, String datasource, String table) { + SQLConfig, List> config = APIJSONApplication.createSQLConfig(); config.setMethod(method); config.setDatabase(database); config.setDatasource(datasource); @@ -91,10 +90,34 @@ public String getUserIdKey(String database, String schema, String datasource, St } + /**获取SQL配置 + * @param table + * @param alias + * @param request + * @param isProcedure + * @return + * @throws Exception + */ + public static , L extends List> SQLConfig newSQLConfig( + RequestMethod method, String table, String alias, M request, List> joinList, boolean isProcedure) throws Exception { + return newSQLConfig(method, table, alias, request, joinList, isProcedure, (SimpleCallback) SIMPLE_CALLBACK); + } + public APIJSONSQLConfig() { + this(RequestMethod.GET); + } + public APIJSONSQLConfig(RequestMethod method) { + super(method); + } + public APIJSONSQLConfig(RequestMethod method, String table) { + super(method, table); + } + public APIJSONSQLConfig(RequestMethod method, int count, int page) { + super(method, count, page); + } - @Override - public String getDBVersion() { + + public String gainDBVersion() { if (isMySQL()) { return "5.7.22"; //"8.0.11"; //TODO 改成你自己的 MySQL 或 PostgreSQL 数据库版本号 //MYSQL 8 和 7 使用的 JDBC 配置不一样 } @@ -110,27 +133,81 @@ public String getDBVersion() { return null; } - @JSONField(serialize = false) // 不在日志打印 账号/密码 等敏感信息,用了 UnitAuto 则一定要加 - @Override - public String getDBUri() { + public String gainDBUri() { if (isMySQL()) { - return "jdbc:mysql://localhost:3306"; //TODO 改成你自己的,TiDB 可以当成 MySQL 使用,默认端口为 4000 + return "jdbc:mysql://localhost:3306"; } - if (isPostgreSQL()) { - return "jdbc:postgresql://localhost:5432/postgres"; //TODO 改成你自己的 + if (isTiDB()) { + return "jdbc:mysql://localhost:4000"; } + if (isPostgreSQL()) { // PG JDBC 必须在 URI 传 catalog + return "jdbc:postgresql://localhost:5432/postgres?stringtype=unspecified"; //TODO 改成你自己的 + } + //if (isCockroachDB()) { // PG JDBC 必须在 URI 传 catalog + // return "jdbc:postgresql://localhost:26257/movr?sslmode=require"; //TODO 改成你自己的 brew install cockroachdb/tap/cockroach && cockroach demo + // // return "jdbc:postgresql://localhost:26258/postgres?sslmode=disable"; //TODO 改成你自己的 brew install cockroachdb/tap/cockroach # && start 3 nodes and init cluster + //} if (isSQLServer()) { return "jdbc:jtds:sqlserver://localhost:1433/pubs;instance=SQLEXPRESS"; //TODO 改成你自己的 } if (isOracle()) { return "jdbc:oracle:thin:@localhost:1521:orcl"; //TODO 改成你自己的 } + if (isDb2()) { + return "jdbc:db2://localhost:50000/BLUDB"; //TODO 改成你自己的 + } + if (isSQLite()) { + return "jdbc:sqlite:sample.db"; //TODO 改成你自己的 + } + if (isDameng()) { + return "jdbc:dm://localhost:5236"; //TODO 改成你自己的 + } + if (isTDengine()) { + // return "jdbc:TAOS://localhost:6030"; //TODO 改成你自己的 + return "jdbc:TAOS-RS://localhost:6041"; //TODO 改成你自己的 + } + if (isTimescaleDB()) { // PG JDBC 必须在 URI 传 catalog + return "jdbc:postgresql://localhost:5432/postgres?stringtype=unspecified"; //TODO 改成你自己的 + } + if (isQuestDB()) { // PG JDBC 必须在 URI 传 catalog + return "jdbc:postgresql://localhost:8812/qdb"; //TODO 改成你自己的 + } + if (isInfluxDB()) { + return "http://203.189.6.3:8086"; //TODO 改成你自己的 + } + if (isMilvus()) { + return "http://localhost:19530"; //TODO 改成你自己的 + } + if (isManticore()) { + return "jdbc:mysql://localhost:9306?characterEncoding=utf8&maxAllowedPacket=512000"; + } + if (isIoTDB()) { + return "jdbc:iotdb://localhost:6667"; // ?charset=GB18030 加参数会报错 URI 格式错误 + } + if (isMongoDB()) { + return "jdbc:mongodb://atlas-sql-6593c65c296c5865121e6ebe-xxskv.a.query.mongodb.net/myVirtualDatabase?ssl=true&authSource=admin"; + } + if (isCassandra()) { + return "http://localhost:7001"; + } + if (isDuckDB()) { + return "jdbc:duckdb:/Users/root/my_database.duckdb"; + } + if (isSurrealDB()) { + // return "memory"; + // return "surrealkv://localhost:8000"; + return "ws://localhost:8000"; + } + if (isOpenGauss()) { + return "jdbc:opengauss://127.0.0.1:5432/postgres?currentSchema=" + DEFAULT_SCHEMA; + } + if (isDoris()) { + return "jdbc:mysql://localhost:9030"; + } return null; } - @JSONField(serialize = false) // 不在日志打印 账号/密码 等敏感信息,用了 UnitAuto 则一定要加 - @Override - public String getDBAccount() { + public String gainDBAccount() { if (isMySQL()) { return "root"; //TODO 改成你自己的 } @@ -143,24 +220,150 @@ public String getDBAccount() { if (isOracle()) { return "scott"; //TODO 改成你自己的 } + if (isMySQL()) { + return "root"; // ""apijson"; //TODO 改成你自己的 + } + if (isPostgreSQL()) { + return "postgres"; //TODO 改成你自己的 + } + //if (isCockroachDB()) { // PG JDBC 必须在 URI 传 catalog + // return "demo"; //TODO 改成你自己的 + // //return "postgres"; //TODO 改成你自己的 + //} + if (isSQLServer()) { + return "sa"; //TODO 改成你自己的 + } + if (isOracle()) { + return "scott"; //TODO 改成你自己的 + } + if (isDb2()) { + return "db2admin"; //TODO 改成你自己的 + } + // if (isSQLite()) { + // return "root"; //TODO 改成你自己的 + // } + if (isDameng()) { + return "SYSDBA"; + } + if (isTDengine()) { + return "root"; //TODO 改成你自己的 + } + //if (isTimescaleDB()) { + // return "postgres"; //TODO 改成你自己的 + //} + if (isQuestDB()) { + return "admin"; //TODO 改成你自己的 + } + if (isInfluxDB()) { + return "iotos"; + } + if (isMilvus()) { + return "root"; + } + if (isManticore()) { + return null; // "root"; + } + if (isIoTDB()) { + return "root"; + } + if (isMongoDB()) { + return "root"; //TODO 改成你自己的 + } + if (isCassandra()) { + return "root"; //TODO 改成你自己的 + } + if (isDuckDB()) { + return "root"; //TODO 改成你自己的 + } + if (isSurrealDB()) { + return "root"; //TODO 改成你自己的 + } + if (isOpenGauss()) { + return "postgres"; //TODO 改成你自己的 + // 不允许用初始账号,需要 CREATE USER 创建新账号并 GRANT 授权 return "opengauss"; //TODO 改成你自己的 + } + if (isDoris()) { + return "root"; //TODO 改成你自己的 + } + return null; } - @JSONField(serialize = false) // 不在日志打印 账号/密码 等敏感信息,用了 UnitAuto 则一定要加 - @Override - public String getDBPassword() { + public String gainDBPassword() { if (isMySQL()) { - return "apijson"; //TODO 改成你自己的,TiDB 可以当成 MySQL 使用, 默认密码为空字符串 "" + return "yourPassword@123"; + } + if (isTiDB()) { + return ""; } if (isPostgreSQL()) { - return null; //TODO 改成你自己的 + return null; } if (isSQLServer()) { - return "apijson@123"; //TODO 改成你自己的 + return "yourPassword@123"; } if (isOracle()) { - return "tiger"; //TODO 改成你自己的 + return "tiger"; + } + //if (isCockroachDB()) { // PG JDBC 必须在 URI 传 catalog + // return "demo39865"; + // // return null + //} + if (isSQLServer()) { + return "yourPassword@123"; + } + if (isOracle()) { + return "tiger"; + } + if (isDb2()) { + return "123"; + } + if (isSQLite()) { + return "yourPassword@123"; + } + if (isDameng()) { + return "SYSDBA"; + } + if (isTDengine()) { + return "taosdata"; + } + if (isTimescaleDB()) { + return "password"; + } + if (isQuestDB()) { + return "quest"; + } + if (isInfluxDB()) { + return "yourPassword@123"; + } + if (isMilvus()) { + return "yourPassword@123"; + } + //if (isManticore()) { + // return null; + //} + //if (isIoTDB()) { + // return "root"; + //} + if (isMongoDB()) { + return "yourPassword@123"; + } + if (isCassandra()) { + return "yourPassword@123"; } + if (isDuckDB()) { + return ""; + } + if (isSurrealDB()) { + return "root"; + } + if (isOpenGauss()) { + return "yourPassword@123"; + } + if (isDoris()) { + return ""; + } + return null; } @@ -183,13 +386,13 @@ public boolean isConfigTable() { return CONFIG_TABLE_LIST.contains(getTable()); } @Override - public String getSQLDatabase() { - String db = isConfigTable() ? getConfigDatabase() : super.getSQLDatabase(); + public String gainSQLDatabase() { + String db = isConfigTable() ? getConfigDatabase() : super.gainSQLDatabase(); return db == null ? DEFAULT_DATABASE : db; } @Override - public String getSQLSchema() { - String sch = isConfigTable() ? getConfigSchema() : super.getSQLSchema(); + public String gainSQLSchema() { + String sch = isConfigTable() ? getConfigSchema() : super.gainSQLSchema(); return sch == null ? DEFAULT_SCHEMA : sch; } @@ -205,49 +408,22 @@ public String getUserIdKey() { } - public APIJSONSQLConfig() { - this(RequestMethod.GET); - } - public APIJSONSQLConfig(RequestMethod method) { - super(method); - } - public APIJSONSQLConfig(RequestMethod method, String table) { - super(method, table); - } - public APIJSONSQLConfig(RequestMethod method, int count, int page) { - super(method, count, page); - } - - - - /**获取SQL配置 - * @param table - * @param alias - * @param request - * @param isProcedure - * @return - * @throws Exception - */ - public static SQLConfig newSQLConfig(RequestMethod method, String table, String alias, JSONObject request, List joinList, boolean isProcedure) throws Exception { - return newSQLConfig(method, table, alias, request, joinList, isProcedure, SIMPLE_CALLBACK); - } - - // 支持 !key 反选字段 和 字段名映射,依赖插件 https://github.com/APIJSON/apijson-column @Override - public AbstractSQLConfig setColumn(List column) { + public APIJSONSQLConfig setColumn(List column) { if (ENABLE_COLUMN_CONFIG) { column = ColumnUtil.compatInputColumn(column, getTable(), getMethod(), getVersion(), ! isConfigTable()); } - return super.setColumn(column); + super.setColumn(column); + return this; } @Override - public String getKey(String key) { + public String gainKey(String key) { if (ENABLE_COLUMN_CONFIG) { - key = ColumnUtil.compatInputKey(key, getTable(), getMethod(), getVersion(), ! isConfigTable()); + key = ColumnUtil.compatInputKey(key, getTable(), getMethod()); } - return super.getKey(key); + return super.gainKey(key); } } diff --git a/src/main/java/apijson/framework/APIJSONSQLExecutor.java b/src/main/java/apijson/framework/APIJSONSQLExecutor.java index 23b390a..ab405f9 100755 --- a/src/main/java/apijson/framework/APIJSONSQLExecutor.java +++ b/src/main/java/apijson/framework/APIJSONSQLExecutor.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,13 +18,11 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.util.List; import java.util.Map; -import apijson.column.ColumnUtil; import org.postgresql.util.PGobject; -import com.alibaba.fastjson.JSONObject; - import apijson.JSON; import apijson.Log; import apijson.NotNull; @@ -35,7 +33,7 @@ /**executor for query(read) or update(write) MySQL database * @author Lemon */ -public class APIJSONSQLExecutor extends AbstractSQLExecutor { +public class APIJSONSQLExecutor, L extends List> extends AbstractSQLExecutor { public static final String TAG = "APIJSONSQLExecutor"; static { @@ -73,8 +71,8 @@ public class APIJSONSQLExecutor extends AbstractSQLExecutor @Override - public PreparedStatement setArgument(@NotNull SQLConfig config, @NotNull PreparedStatement statement, int index, Object value) throws SQLException { - if (config.isPostgreSQL() && JSON.isBooleanOrNumberOrString(value) == false) { + public PreparedStatement setArgument(@NotNull SQLConfig config, @NotNull PreparedStatement statement, int index, Object value) throws SQLException { + if (config.isPostgreSQL() && JSON.isBoolOrNumOrStr(value) == false) { PGobject o = new PGobject(); o.setType("jsonb"); o.setValue(value == null ? null : value.toString()); @@ -87,20 +85,24 @@ public PreparedStatement setArgument(@NotNull SQLConfig config, @NotNull Prepare @Override - protected Object getValue(SQLConfig config, ResultSet rs, ResultSetMetaData rsmd, int tablePosition, - JSONObject table, int columnIndex, String lable, Map childMap) throws Exception { + protected Object getValue( + SQLConfig config, ResultSet rs, ResultSetMetaData rsmd, int row + , M table, int columnIndex, String label, Map childMap, Map keyMap + ) throws Exception { - Object value = super.getValue(config, rs, rsmd, tablePosition, table, columnIndex, lable, childMap); + Object value = super.getValue(config, rs, rsmd, row, table, columnIndex, label, childMap, keyMap); return value instanceof PGobject ? JSON.parse(((PGobject) value).getValue()) : value; } // 支持 !key 反选字段 和 字段名映射,依赖插件 https://github.com/APIJSON/apijson-column @Override - protected String getKey(SQLConfig config, ResultSet rs, ResultSetMetaData rsmd, int tablePosition, JSONObject table, - int columnIndex, Map childMap) throws Exception { + protected String getKey( + SQLConfig config, ResultSet rs, ResultSetMetaData rsmd, int row + , M table, int columnIndex, Map childMap, Map keyMap + ) throws Exception { - String key = super.getKey(config, rs, rsmd, tablePosition, table, columnIndex, childMap); + String key = super.getKey(config, rs, rsmd, row, table, columnIndex, childMap, keyMap); if (APIJSONSQLConfig.ENABLE_COLUMN_CONFIG) { return ColumnUtil.compatOutputKey(key, config.getTable(), config.getMethod()); } diff --git a/src/main/java/apijson/framework/APIJSONVerifier.java b/src/main/java/apijson/framework/APIJSONVerifier.java index b558da9..b0fcc11 100755 --- a/src/main/java/apijson/framework/APIJSONVerifier.java +++ b/src/main/java/apijson/framework/APIJSONVerifier.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,36 +14,31 @@ package apijson.framework; -import static apijson.framework.APIJSONConstant.ACCESS_; -import static apijson.framework.APIJSONConstant.REQUEST_; -import static apijson.framework.APIJSONConstant.VISITOR_; -import static apijson.framework.APIJSONConstant.VISITOR_ID; +import static apijson.JSON.*; +import static apijson.JSONMap.KEY_ORDER; +import static apijson.JSONMap.isTableKey; +import static apijson.JSONRequest.KEY_COUNT; +import static apijson.framework.APIJSONConstant.*; +import static apijson.framework.APIJSONConstant.METHODS; import java.rmi.ServerException; import java.util.*; -import javax.servlet.http.HttpSession; +import apijson.*; +import apijson.orm.JSONRequest; +import jakarta.servlet.http.HttpSession; -import apijson.column.ColumnUtil; import apijson.orm.*; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; - -import apijson.JSON; -import apijson.JSONResponse; -import apijson.Log; -import apijson.NotNull; -import apijson.RequestMethod; -import apijson.StringUtil; /**权限验证器 * @author Lemon */ -public class APIJSONVerifier extends AbstractVerifier { +public class APIJSONVerifier, L extends List> extends AbstractVerifier { public static final String TAG = "APIJSONVerifier"; public static boolean ENABLE_VERIFY_COLUMN = true; + public static boolean ENABLE_APIJSON_ROUTER = false; // 由 init 方法读取数据库 Access 表来替代手动输入配置 // // > @@ -57,17 +52,17 @@ public class APIJSONVerifier extends AbstractVerifier { // ACCESS_MAP.put(Login.class.getSimpleName(), getAccessMap(Login.class.getAnnotation(MethodAccess.class))); // } - public static APIJSONCreator APIJSON_CREATOR; + public static Map>> DOCUMENT_MAP; static { - APIJSON_CREATOR = new APIJSONCreator<>(); + DOCUMENT_MAP = new HashMap<>(); } /**初始化,加载所有权限配置和请求校验配置 * @return * @throws ServerException */ - public static JSONObject init() throws ServerException { + public static , L extends List> M init() throws ServerException { return init(false); } @@ -76,8 +71,10 @@ public static JSONObject init() throws ServerException { * @return * @throws ServerException */ - public static JSONObject init(boolean shutdownWhenServerError) throws ServerException { - return init(shutdownWhenServerError, null); + public static , L extends List> M init(boolean shutdownWhenServerError) throws ServerException { + return init(shutdownWhenServerError, new APIJSONCreator() { + + }); } /**初始化,加载所有权限配置和请求校验配置 @@ -85,7 +82,7 @@ public static JSONObject init(boolean shutdownWhenServerError) throws ServerExce * @return * @throws ServerException */ - public static JSONObject init(APIJSONCreator creator) throws ServerException { + public static , L extends List> M init(APIJSONCreator creator) throws ServerException { return init(false, creator); } @@ -95,8 +92,9 @@ public static JSONObject init(APIJSONCreator creator) throws ServerExcept * @return * @throws ServerException */ - public static JSONObject init(boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { - JSONObject result = new JSONObject(true); + public static , L extends List> M init( + boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { + M result = JSON.createJSONObject(); if (ENABLE_VERIFY_ROLE) { result.put(ACCESS_, initAccess(shutdownWhenServerError, creator)); } @@ -110,7 +108,7 @@ public static JSONObject init(boolean shutdownWhenServerError, APIJSONCreato * @return * @throws ServerException */ - public static JSONObject initAccess() throws ServerException { + public static , L extends List> M initAccess() throws ServerException { return initAccess(false); } @@ -119,7 +117,7 @@ public static JSONObject initAccess() throws ServerException { * @return * @throws ServerException */ - public static JSONObject initAccess(boolean shutdownWhenServerError) throws ServerException { + public static , L extends List> M initAccess(boolean shutdownWhenServerError) throws ServerException { return initAccess(shutdownWhenServerError, null); } @@ -128,7 +126,7 @@ public static JSONObject initAccess(boolean shutdownWhenServerError) throws Serv * @return * @throws ServerException */ - public static JSONObject initAccess(APIJSONCreator creator) throws ServerException { + public static , L extends List> M initAccess(APIJSONCreator creator) throws ServerException { return initAccess(false, creator); } @@ -138,7 +136,8 @@ public static JSONObject initAccess(APIJSONCreator creator) throws Server * @return * @throws ServerException */ - public static JSONObject initAccess(boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { + public static , L extends List> M initAccess( + boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { return initAccess(shutdownWhenServerError, creator, null); } @@ -150,33 +149,32 @@ public static JSONObject initAccess(boolean shutdownWhenServerError, APIJSON * @throws ServerException */ @SuppressWarnings("unchecked") - public static JSONObject initAccess(boolean shutdownWhenServerError, APIJSONCreator creator, JSONObject table) throws ServerException { + public static , L extends List> M initAccess( + boolean shutdownWhenServerError, APIJSONCreator creator, M table) throws ServerException { if (creator == null) { - creator = (APIJSONCreator) APIJSON_CREATOR; + creator = (APIJSONCreator) APIJSONApplication.DEFAULT_APIJSON_CREATOR; } - APIJSON_CREATOR = creator; - boolean isAll = table == null || table.isEmpty(); - JSONObject access = isAll ? new JSONRequest() : table; + M access = isAll ? JSON.createJSONObject() : table; if (Log.DEBUG == false) { access.put(APIJSONConstant.KEY_DEBUG, 0); } - JSONRequest accessItem = new JSONRequest(); + M accessItem = JSON.createJSONObject(); accessItem.put(ACCESS_, access); + accessItem.put(KEY_COUNT, 0); - JSONRequest request = new JSONRequest(); - request.putAll(accessItem.toArray(0, 0, ACCESS_)); - + M request = JSON.createJSONObject(); + request.put(ACCESS_ + "[]", accessItem); - JSONObject response = creator.createParser().setMethod(RequestMethod.GET).setNeedVerify(false).parseResponse(request); + M response = creator.createParser().setMethod(RequestMethod.GET).setNeedVerify(false).parseResponse(request); if (JSONResponse.isSuccess(response) == false) { - Log.e(TAG, "\n\n\n\n\n !!!! 查询权限配置异常 !!!\n" + response.getString(JSONResponse.KEY_MSG) + "\n\n\n\n\n"); + Log.e(TAG, "\n\n\n\n\n !!!! 查询权限配置异常 !!!\n" + getString(response, JSONResponse.KEY_MSG) + "\n\n\n\n\n"); onServerError("查询权限配置异常 !", shutdownWhenServerError); } - JSONArray list = response.getJSONArray(ACCESS_ + "[]"); + L list = getJSONArray(response, ACCESS_ + "[]"); int size = list == null ? 0 : list.size(); if (isAll && size <= 0) { Log.w(TAG, "initAccess isAll && size <= 0,,没有可用的权限配置"); @@ -187,45 +185,69 @@ public static JSONObject initAccess(boolean shutdownWhenServerError, APIJSON Map> newMap = new LinkedHashMap<>(); Map> fakeDeleteMap = new LinkedHashMap<>(); - Map newTKMap = new LinkedHashMap<>(); + Map newTKMap = new LinkedHashMap<>(); // JSON.createJSONObject(); + Map tableSchemaMap = new LinkedHashMap<>(); // JSON.createJSONObject(); SortedMap>> versionedTableColumnMap = new TreeMap<>(ColumnUtil.DESC_COMPARATOR); SortedMap>> versionedKeyColumnMap = new TreeMap<>(ColumnUtil.DESC_COMPARATOR); for (int i = 0; i < size; i++) { - JSONObject item = list.getJSONObject(i); + M item = getJSONObject(list, i); if (item == null) { continue; } Map map = new HashMap<>(); - map.put(RequestMethod.GET, JSON.parseObject(item.getString("get"), String[].class)); - map.put(RequestMethod.HEAD, JSON.parseObject(item.getString("head"), String[].class)); - map.put(RequestMethod.GETS, JSON.parseObject(item.getString("gets"), String[].class)); - map.put(RequestMethod.HEADS, JSON.parseObject(item.getString("heads"), String[].class)); - map.put(RequestMethod.POST, JSON.parseObject(item.getString("post"), String[].class)); - map.put(RequestMethod.PUT, JSON.parseObject(item.getString("put"), String[].class)); - map.put(RequestMethod.DELETE, JSON.parseObject(item.getString("delete"), String[].class)); - - String name = item.getString("name"); - String alias = item.getString("alias"); - - Map fakemap = new HashMap<>(); - String deletedKey = item.getString(AbstractSQLConfig.KEY_DELETED_KEY); + // fastjson2 不支持 + //map.put(RequestMethod.GET, JSON.parseObject(getString(item, "get"), String[].class)); + //map.put(RequestMethod.HEAD, JSON.parseObject(getString(item, "head"), String[].class)); + //map.put(RequestMethod.GETS, JSON.parseObject(getString(item, "gets"), String[].class)); + //map.put(RequestMethod.HEADS, JSON.parseObject(getString(item, "heads"), String[].class)); + //map.put(RequestMethod.POST, JSON.parseObject(getString(item, "post"), String[].class)); + //map.put(RequestMethod.PUT, JSON.parseObject(getString(item, "put"), String[].class)); + //map.put(RequestMethod.DELETE, JSON.parseObject(getString(item, "delete"), String[].class)); + + List getArr = parseArray(getString(item, "get"), String.class); + map.put(RequestMethod.GET, getArr == null ? null : getArr.toArray(new String[]{})); + + List headArr = parseArray(getString(item, "head"), String.class); + map.put(RequestMethod.HEAD, headArr == null ? null : headArr.toArray(new String[]{})); + + List getsArr = parseArray(getString(item, "gets"), String.class); + map.put(RequestMethod.GETS, getsArr == null ? null : getsArr.toArray(new String[]{})); + + List headsArr = parseArray(getString(item, "heads"), String.class); + map.put(RequestMethod.HEADS, headsArr == null ? null : headsArr.toArray(new String[]{})); + + List postArr = parseArray(getString(item, "post"), String.class); + map.put(RequestMethod.POST, postArr == null ? null : postArr.toArray(new String[]{})); + + List putArr = parseArray(getString(item, "put"), String.class); + map.put(RequestMethod.PUT, putArr == null ? null : putArr.toArray(new String[]{})); + + List deleteArr = parseArray(getString(item, "delete"), String.class); + map.put(RequestMethod.DELETE, deleteArr == null ? null : deleteArr.toArray(new String[]{})); + + String name = getString(item, "name"); + String alias = getString(item, "alias"); + String schema = getString(item, "schema"); + + Map fakeMap = new LinkedHashMap<>(); + String deletedKey = getString(item, AbstractSQLConfig.KEY_DELETED_KEY); if(StringUtil.isNotEmpty(deletedKey, true)) { boolean containNotDeletedValue = item.containsKey(AbstractSQLConfig.KEY_NOT_DELETED_VALUE); - Object deletedValue = item.getString(AbstractSQLConfig.KEY_DELETED_VALUE); + Object deletedValue = getString(item, AbstractSQLConfig.KEY_DELETED_VALUE); if (containNotDeletedValue == false && StringUtil.isEmpty(deletedValue, true)) { onServerError( - "Access表 id = " + item.getString("id") + " 对应的 " + "Access表 id = " + getString(item, "id") + " 对应的 " + AbstractSQLConfig.KEY_DELETED_VALUE + " 的值不能为空!或者必须包含字段 " + AbstractSQLConfig.KEY_NOT_DELETED_VALUE + " !" , shutdownWhenServerError ); } - fakemap.put(AbstractSQLConfig.KEY_DELETED_KEY, deletedKey); - fakemap.put(AbstractSQLConfig.KEY_DELETED_VALUE, deletedValue); + fakeMap.put(AbstractSQLConfig.KEY_DELETED_KEY, deletedKey); + fakeMap.put(AbstractSQLConfig.KEY_DELETED_VALUE, deletedValue); if (containNotDeletedValue) { - fakemap.put(AbstractSQLConfig.KEY_NOT_DELETED_VALUE, item.get(AbstractSQLConfig.KEY_NOT_DELETED_VALUE)); + fakeMap.put(AbstractSQLConfig.KEY_NOT_DELETED_VALUE, item.get(AbstractSQLConfig.KEY_NOT_DELETED_VALUE)); } } @@ -239,21 +261,22 @@ public static JSONObject initAccess(boolean shutdownWhenServerError, APIJSON } if (StringUtil.isEmpty(alias, true)) { - if (JSONRequest.isTableKey(name) == false) { + if (isTableKey(name) == false) { onServerError("name: " + name + "不合法!字段 alias 的值为空时,name 必须为合法表名!", shutdownWhenServerError); } alias = name; - } else if (JSONRequest.isTableKey(alias) == false) { + } else if (isTableKey(alias) == false) { onServerError("alias: " + alias + "不合法!字段 alias 的值只能为 空 或者 合法表名!", shutdownWhenServerError); } newMap.put(alias, map); - fakeDeleteMap.put(alias, fakemap); + fakeDeleteMap.put(alias, fakeMap); newTKMap.put(alias, name); + tableSchemaMap.put(alias, schema); if (ENABLE_VERIFY_COLUMN) { - JSONObject columns = item.getJSONObject("columns"); + M columns = getJSONObject(item, "columns"); Set> set = columns == null ? null : columns.entrySet(); if (set != null) { @@ -313,22 +336,24 @@ public static JSONObject initAccess(boolean shutdownWhenServerError, APIJSON ACCESS_MAP = newMap; ACCESS_FAKE_DELETE_MAP = fakeDeleteMap; APIJSONSQLConfig.TABLE_KEY_MAP = newTKMap; + APIJSONSQLConfig.TABLE_SCHEMA_MAP = tableSchemaMap; } else { ACCESS_MAP.putAll(newMap); ACCESS_FAKE_DELETE_MAP.putAll(fakeDeleteMap); APIJSONSQLConfig.TABLE_KEY_MAP.putAll(newTKMap); + APIJSONSQLConfig.TABLE_SCHEMA_MAP = tableSchemaMap; } - if (ENABLE_VERIFY_COLUMN) { - if (isAll) { // 全量更新 - ColumnUtil.VERSIONED_TABLE_COLUMN_MAP = versionedTableColumnMap; - ColumnUtil.VERSIONED_KEY_COLUMN_MAP = versionedKeyColumnMap; - } else { - ColumnUtil.VERSIONED_TABLE_COLUMN_MAP.putAll(versionedTableColumnMap); - ColumnUtil.VERSIONED_KEY_COLUMN_MAP.putAll(versionedKeyColumnMap); - } - ColumnUtil.init(); - } +// if (ENABLE_VERIFY_COLUMN) { +// if (isAll) { // 全量更新 +// ColumnUtil.VERSIONED_TABLE_COLUMN_MAP = versionedTableColumnMap; +// ColumnUtil.VERSIONED_KEY_COLUMN_MAP = versionedKeyColumnMap; +// } else { +// ColumnUtil.VERSIONED_TABLE_COLUMN_MAP.putAll(versionedTableColumnMap); +// ColumnUtil.VERSIONED_KEY_COLUMN_MAP.putAll(versionedKeyColumnMap); +// } +// ColumnUtil.init(); +// } Log.d(TAG, "initAccess for /> ACCESS_MAP.size() = " + ACCESS_MAP.size() + " >>>>>>>>>>>>>>>>>>>>>>>"); @@ -340,7 +365,7 @@ public static JSONObject initAccess(boolean shutdownWhenServerError, APIJSON * @return * @throws ServerException */ - public static JSONObject initRequest() throws ServerException { + public static , L extends List> M initRequest() throws ServerException { return initRequest(false); } @@ -349,7 +374,7 @@ public static JSONObject initRequest() throws ServerException { * @return * @throws ServerException */ - public static JSONObject initRequest(boolean shutdownWhenServerError) throws ServerException { + public static , L extends List> M initRequest(boolean shutdownWhenServerError) throws ServerException { return initRequest(shutdownWhenServerError, null); } @@ -358,7 +383,8 @@ public static JSONObject initRequest(boolean shutdownWhenServerError) throws Ser * @return * @throws ServerException */ - public static JSONObject initRequest(APIJSONCreator creator) throws ServerException { + public static , L extends List> M initRequest( + APIJSONCreator creator) throws ServerException { return initRequest(false, creator); } @@ -368,7 +394,8 @@ public static JSONObject initRequest(APIJSONCreator creator) throws Serve * @return * @throws ServerException */ - public static JSONObject initRequest(boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { + public static , L extends List> M initRequest( + boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { return initRequest(shutdownWhenServerError, creator, null); } @@ -380,33 +407,34 @@ public static JSONObject initRequest(boolean shutdownWhenServerError, APIJSO * @throws ServerException */ @SuppressWarnings("unchecked") - public static JSONObject initRequest(boolean shutdownWhenServerError, APIJSONCreator creator, JSONObject table) throws ServerException { + public static , L extends List> M initRequest( + boolean shutdownWhenServerError, APIJSONCreator creator, M table) throws ServerException { if (creator == null) { - creator = (APIJSONCreator) APIJSON_CREATOR; + creator = (APIJSONCreator) APIJSONApplication.DEFAULT_APIJSON_CREATOR; } - APIJSON_CREATOR = creator; - boolean isAll = table == null || table.isEmpty(); - JSONObject requestTable = isAll ? new JSONRequest().setOrder("version-,id+") : table; + M tblObj = createJSONObject(); + tblObj.put(KEY_ORDER, "version-,id+"); + M requestTable = isAll ? tblObj : table; if (Log.DEBUG == false) { requestTable.put(APIJSONConstant.KEY_DEBUG, 0); } - JSONRequest requestItem = new JSONRequest(); + M requestItem = JSON.createJSONObject(); requestItem.put(REQUEST_, requestTable); // 方便查找 + requestItem.put(KEY_COUNT, 0); - JSONRequest request = new JSONRequest(); - request.putAll(requestItem.toArray(0, 0, REQUEST_)); - - - JSONObject response = creator.createParser().setMethod(RequestMethod.GET).setNeedVerify(false).parseResponse(request); + M request = JSON.createJSONObject(); + request.put(REQUEST_ + "[]", requestItem); + + M response = creator.createParser().setMethod(RequestMethod.GET).setNeedVerify(false).parseResponse(request); if (JSONResponse.isSuccess(response) == false) { - Log.e(TAG, "\n\n\n\n\n !!!! 查询请求校验规则配置异常 !!!\n" + response.getString(JSONResponse.KEY_MSG) + "\n\n\n\n\n"); + Log.e(TAG, "\n\n\n\n\n !!!! 查询请求校验规则配置异常 !!!\n" + getString(response, JSONResponse.KEY_MSG) + "\n\n\n\n\n"); onServerError("查询请求校验规则配置异常 !", shutdownWhenServerError); } - JSONArray list = response.getJSONArray(REQUEST_ + "[]"); + L list = getJSONArray(response, REQUEST_ + "[]"); int size = list == null ? 0 : list.size(); if (isAll && size <= 0) { Log.w(TAG, "initRequest isAll && size <= 0,没有可用的请求校验规则配置"); @@ -415,48 +443,48 @@ public static JSONObject initRequest(boolean shutdownWhenServerError, APIJSO Log.d(TAG, "initRequest < for REQUEST_MAP.size() = " + REQUEST_MAP.size() + " <<<<<<<<<<<<<<<<<<<<<<<<"); - Map> newMap = new LinkedHashMap<>(); + Map>> newMap = new LinkedHashMap<>(); for (int i = 0; i < size; i++) { - JSONObject item = list.getJSONObject(i); + M item = getJSONObject(list, i); if (item == null) { continue; } - String version = item.getString("version"); + String version = getString(item, "version"); if (StringUtil.isEmpty(version, true)) { Log.e(TAG, "initRequest for StringUtil.isEmpty(version, true),Request 表中的 version 不能为空!"); onServerError("服务器内部错误,Request 表中的 version 不能为空!", shutdownWhenServerError); } - String method = item.getString("method"); + String method = getString(item, "method"); if (StringUtil.isEmpty(method, true)) { Log.e(TAG, "initRequest for StringUtil.isEmpty(method, true),Request 表中的 method 不能为空!"); onServerError("服务器内部错误,Request 表中的 method 不能为空!", shutdownWhenServerError); } - String tag = item.getString("tag"); + String tag = getString(item, "tag"); if (StringUtil.isEmpty(tag, true)) { Log.e(TAG, "initRequest for StringUtil.isEmpty(tag, true),Request 表中的 tag 不能为空!"); onServerError("服务器内部错误,Request 表中的 tag 不能为空!", shutdownWhenServerError); } - JSONObject structure = JSON.parseObject(item.getString("structure")); + M structure = JSON.parseObject(getString(item, "structure")); - JSONObject target = null; + M target = null; if (structure != null) { target = structure; if (structure.containsKey(tag) == false) { //tag 是 Table 名或 Table[] - boolean isArrayKey = tag.endsWith(":[]"); // JSONRequest.isArrayKey(tag); + boolean isArrayKey = tag.endsWith(":[]"); // apijson.isArrayKey(tag); String key = isArrayKey ? tag.substring(0, tag.length() - 3) : tag; - if (apijson.JSONObject.isTableKey(key)) { + if (isTableKey(key)) { if (isArrayKey) { //自动为 tag = Comment:[] 的 { ... } 新增键值对 "Comment[]":[] 为 { "Comment[]":[], ... } - target.put(key + "[]", new JSONArray()); + target.put(key + "[]", JSON.createJSONArray()); } else { //自动为 tag = Comment 的 { ... } 包一层为 { "Comment": { ... } } - target = new JSONObject(true); + target = JSON.createJSONObject(); target.put(tag, structure); } } @@ -469,7 +497,7 @@ public static JSONObject initRequest(boolean shutdownWhenServerError, APIJSO } String cacheKey = getCacheKeyForRequest(method, tag); - SortedMap versionedMap = newMap.get(cacheKey); + SortedMap> versionedMap = newMap.get(cacheKey); if (versionedMap == null) { versionedMap = new TreeMap<>(new Comparator() { @@ -484,7 +512,8 @@ public int compare(Integer o1, Integer o2) { } if (isAll) { // 全量更新 - REQUEST_MAP = newMap; + REQUEST_MAP = new LinkedHashMap<>(); + REQUEST_MAP.putAll(newMap); } else { REQUEST_MAP.putAll(newMap); } @@ -495,6 +524,179 @@ public int compare(Integer o1, Integer o2) { } + + /**初始化,加载所有请求校验配置 + * @return + * @throws ServerException + */ + public static > M initDocument() throws ServerException { + return initDocument(false); + } + /**初始化,加载所有请求校验配置 + * @param shutdownWhenServerError + * @return + * @throws ServerException + */ + public static > M initDocument(boolean shutdownWhenServerError) throws ServerException { + return initDocument(shutdownWhenServerError, null); + } + /**初始化,加载所有请求校验配置 + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M initDocument(APIJSONCreator creator) throws ServerException { + return initDocument(false, creator); + } + /**初始化,加载所有请求校验配置 + * @param shutdownWhenServerError + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M initDocument( + boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { + return initDocument(shutdownWhenServerError, creator, null); + } + /**初始化,加载所有请求校验配置 + * @param shutdownWhenServerError + * @param creator + * @param table 表内自定义数据过滤条件 + * @return + * @throws ServerException + */ + @SuppressWarnings("unchecked") + public static , L extends List> M initDocument( + boolean shutdownWhenServerError, APIJSONCreator creator, M table) throws ServerException { + if (creator == null) { + creator = (APIJSONCreator) APIJSONApplication.DEFAULT_APIJSON_CREATOR; + } + + boolean isAll = table == null || table.isEmpty(); + M document = isAll ? JSON.createJSONObject(new JSONRequest("apijson{}", "length(apijson)>0").setOrder("version-,id+")) : table; + if (Log.DEBUG == false) { + document.put(APIJSONConstant.KEY_DEBUG, 0); + } + + M requestItem = JSON.createJSONObject(); + requestItem.put(DOCUMENT_, document); // 方便查找 + requestItem.put(KEY_COUNT, 0); + + M request = JSON.createJSONObject(); + request.put(DOCUMENT_ + "[]", requestItem); + + M response = creator.createParser().setMethod(RequestMethod.GET).setNeedVerify(false).parseResponse(request); + if (JSONResponse.isSuccess(response) == false) { + Log.e(TAG, "\n\n\n\n\n !!!! 查询请求映射配置异常 !!!\n" + getString(response, JSONResponse.KEY_MSG) + "\n\n\n\n\n"); + onServerError("查询请求映射配置异常 !", shutdownWhenServerError); + } + + L list = getJSONArray(response, DOCUMENT_ + "[]"); + int size = list == null ? 0 : list.size(); + if (isAll && size <= 0) { + Log.w(TAG, "initDocument isAll && size <= 0,没有可用的请求映射配置"); + return response; + } + + Log.d(TAG, "initDocument < for DOCUMENT_MAP.size() = " + DOCUMENT_MAP.size() + " <<<<<<<<<<<<<<<<<<<<<<<<"); + + + Map>> newMap = new LinkedHashMap<>(); + + for (int i = 0; i < size; i++) { + M item = getJSONObject(list, i); + if (item == null) { + continue; + } + + String version = getString(item, "version"); + if (StringUtil.isEmpty(version, true)) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + + " 对应 version 不能为空!", shutdownWhenServerError); + } + + String url = getString(item, "url"); + int index = url == null ? -1 : url.indexOf("/"); + if (index != 0) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应 url 值错误,必须以 / 开头!", shutdownWhenServerError); + } + + String requestStr = getString(item, "request"); + + String apijson = getString(item, "apijson"); + if (StringUtil.isEmpty(apijson)) { + if (StringUtil.isBranchUrl(url) == false) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应 url 值错误!只允许合法的 URL 格式!", shutdownWhenServerError); + } + } + else { + if (StringUtil.isNotEmpty(requestStr)) { + try { + JSON.parseObject(requestStr); + } + catch (Exception e) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应 request 值 " + requestStr + " 错误!只允许合法的 M 格式!" + e.getMessage(), shutdownWhenServerError); + } + } + + try { + JSON.parseObject(apijson); + } + catch (Exception e) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应 apijson 值 " + apijson + " 错误!只允许合法的 M 格式!" + e.getMessage(), shutdownWhenServerError); + } + + index = url.lastIndexOf("/"); + String method = index < 0 ? null : url.substring(0, index); + String tag = index < 0 ? null : url.substring(index + 1); + + index = method == null ? -1 : method.lastIndexOf("/"); + method = index < 0 ? method : method.substring(index + 1); + + if (METHODS.contains(method) == false) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应路径 /{method}/{tag} 中 method 值 " + method + " 错误!apijson 字段不为空时只允许 " + METHODS + " 中的一个!", shutdownWhenServerError); + } + + if (StringUtil.isName(tag) == false) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应路径 /{method}/{tag} 中 tag 值 " + tag + " 错误!apijson 字段不为空时只允许变量命名格式!", shutdownWhenServerError); + } + + String cacheKey = getCacheKeyForRequest(method, tag); + SortedMap> versionedMap = newMap.get(cacheKey); + if (versionedMap == null) { + versionedMap = new TreeMap<>(new Comparator() { + + @Override + public int compare(Integer o1, Integer o2) { + return o2 == null ? -1 : o2.compareTo(o1); // 降序 + } + }); + } + versionedMap.put(Integer.valueOf(version), item); + newMap.put(cacheKey, versionedMap); + } + + } + + if (isAll) { // 全量更新 + DOCUMENT_MAP = newMap; + } + else { + DOCUMENT_MAP.putAll(newMap); + } + + Log.d(TAG, "initDocument for /> DOCUMENT_MAP.size() = " + DOCUMENT_MAP.size() + " >>>>>>>>>>>>>>>>>>>>>>>"); + + return response; + } + + public static void test() throws Exception { testStructure(); } @@ -506,11 +708,13 @@ public static void test() throws Exception { * 测试 Request 和 Response 的数据结构校验 * @throws Exception */ - public static void testStructure() throws Exception { - JSONObject request; + public static , L extends List> void testStructure() throws Exception { + Parser parser = APIJSONApplication.createParser(); + + M request; try { request = JSON.parseObject("{\"Comment\":{\"userId\":0}}"); - Log.d(TAG, "test verifyRequest = " + AbstractVerifier.verifyRequest(RequestMethod.POST, "", JSON.parseObject(requestConfig), request, APIJSON_CREATOR)); + Log.d(TAG, "test verifyRequest = " + AbstractVerifier.verifyRequest(RequestMethod.POST, "", JSON.parseObject(requestConfig), request, parser)); } catch (Throwable e) { if (e instanceof IllegalArgumentException == false || "POST请求,Comment 里面不能缺少 momentId 等[userId,momentId,content]内的任何字段!".equals(e.getMessage()) == false) { throw e; @@ -519,7 +723,7 @@ public static void testStructure() throws Exception { } try { request = JSON.parseObject("{\"Comment\":{\"id\":0, \"userId\":0, \"momentId\":0, \"content\":\"apijson\"}}"); - Log.d(TAG, "test verifyRequest = " + AbstractVerifier.verifyRequest(RequestMethod.POST, "", JSON.parseObject(requestConfig), request, APIJSON_CREATOR)); + Log.d(TAG, "test verifyRequest = " + AbstractVerifier.verifyRequest(RequestMethod.POST, "", JSON.parseObject(requestConfig), request, parser)); } catch (Throwable e) { if (e instanceof IllegalArgumentException == false || "POST请求,/Comment 不能传 id !".equals(e.getMessage()) == false) { throw e; @@ -528,43 +732,43 @@ public static void testStructure() throws Exception { } try { request = JSON.parseObject("{\"Comment\":{\"userId\":0, \"momentId\":0, \"content\":\"apijson\"}}"); - Log.d(TAG, "test verifyRequest = " + AbstractVerifier.verifyRequest(RequestMethod.POST, "", JSON.parseObject(requestConfig), request, APIJSON_CREATOR)); - AssertUtil.assertEqual("OWNER", request.getString("@role")); + Log.d(TAG, "test verifyRequest = " + AbstractVerifier.verifyRequest(RequestMethod.POST, "", JSON.parseObject(requestConfig), request, parser)); + AssertUtil.assertEqual("OWNER", getString(request, "@role")); Log.d(TAG, "测试 Operation.INSERT 不存在字段时插入:成功"); } catch (Throwable e) { throw e; } - JSONObject response; + M response; try { response = JSON.parseObject("{\"User\":{\"userId\":0}}"); - Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, APIJSON_CREATOR, null)); - AssertUtil.assertEqual("verifyURLList(pictureList)", response.getJSONObject("User").getString("verifyURLList-()")); + Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, parser, null)); + AssertUtil.assertEqual("verifyURLList(pictureList)", getJSONObject(response, "User").get("verifyURLList-()")); Log.d(TAG, "测试 Operation.UPDATE 强制插入/替换:成功"); } catch (Throwable e) { throw e; } try { response = JSON.parseObject("{\"User\":{\"userId\":0, \"phone\":\"12345678\"}}"); - Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, APIJSON_CREATOR, null)); - AssertUtil.assertEqual(null, response.getJSONObject("User").get("phone")); + Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, parser, null)); + AssertUtil.assertEqual(null, getJSONObject(response, "User").get("phone")); Log.d(TAG, "测试 Operation.REMOVE 强制移除:成功"); } catch (Throwable e) { throw e; } try { response = JSON.parseObject("{\"User\":{\"userId\":0, \"phone\":\"12345678\", \"sex\":1}}"); - Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, APIJSON_CREATOR, null)); - AssertUtil.assertEqual("api", response.getJSONObject("User").get("name")); + Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, parser, null)); + AssertUtil.assertEqual("api", getJSONObject(response, "User").get("name")); Log.d(TAG, "测试 Operation.INSERT 不存在字段时插入:成功"); } catch (Throwable e) { throw e; } try { response = JSON.parseObject("{\"User\":{\"id\":0, \"name\":\"tommy\", \"phone\":\"12345678\", \"sex\":1}}"); - Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, APIJSON_CREATOR, null)); - AssertUtil.assertEqual(2, response.getJSONObject("User").get("sex")); + Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, parser, null)); + AssertUtil.assertEqual(2, getJSONObject(response, "User").get("sex")); Log.d(TAG, "测试 Operation.REPLACE 存在字段时替换:成功"); } catch (Throwable e) { throw e; @@ -583,12 +787,26 @@ protected static void onServerError(String msg, boolean shutdown) throws ServerE } } + protected Parser parser; + @Override + public Parser getParser() { + if (parser == null) { + parser = createParser(); + } + return parser; + } + + @Override + public APIJSONVerifier setParser(AbstractParser parser) { + this.parser = parser; + return this; + } @SuppressWarnings("unchecked") @NotNull @Override - public APIJSONParser createParser() { - APIJSONParser parser = (APIJSONParser) APIJSON_CREATOR.createParser(); + public APIJSONParser createParser() { + APIJSONParser parser = APIJSONApplication.createParser(); parser.setVisitor(visitor); return parser; } @@ -601,7 +819,7 @@ public APIJSONParser createParser() { */ public static void verifyLogin(HttpSession session) throws Exception { Log.d(TAG, "verifyLogin session.getId() = " + (session == null ? null : session.getId())); - APIJSON_CREATOR.createVerifier().setVisitor(getVisitor(session)).verifyLogin(); + APIJSONApplication.createVerifier().setVisitor(getVisitor(session)).verifyLogin(); } @@ -611,7 +829,7 @@ public static void verifyLogin(HttpSession session) throws Exception { * @return */ @SuppressWarnings("unchecked") - public static T getVisitorId(HttpSession session) { + public static T getVisitorId(HttpSession session) { if (session == null) { return null; } @@ -629,7 +847,7 @@ public static T getVisitorId(HttpSession session) { * @return */ @SuppressWarnings("unchecked") - public static Visitor getVisitor(HttpSession session) { + public static Visitor getVisitor(HttpSession session) { return session == null ? null : (Visitor) session.getAttribute(VISITOR_); } @@ -650,6 +868,4 @@ public T newId(RequestMethod method, String database, String schema, String data return (T) APIJSONSQLConfig.SIMPLE_CALLBACK.newId(method, database, schema, datasource, table); } - - } diff --git a/src/main/java/apijson/framework/AssertUtil.java b/src/main/java/apijson/framework/AssertUtil.java index fd0c1c1..5375ba1 100644 --- a/src/main/java/apijson/framework/AssertUtil.java +++ b/src/main/java/apijson/framework/AssertUtil.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ public static void assertEqual(Object a, Object b, String errorMessage) { return; } - if (a == null || b == null || a.equals(b) == false) { + if (a == null || !a.equals(b)) { throw new AssertionError(errorMessage == null ? "assert fail: a != b" : errorMessage); } } diff --git a/src/main/java/apijson/framework/BaseModel.java b/src/main/java/apijson/framework/BaseModel.java index f50dba7..56dd85c 100755 --- a/src/main/java/apijson/framework/BaseModel.java +++ b/src/main/java/apijson/framework/BaseModel.java @@ -1,4 +1,4 @@ -/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON) +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,8 +21,7 @@ import java.util.Date; import java.util.Map; -import com.alibaba.fastjson.JSON; - +import apijson.JSON; import apijson.StringUtil; /**base model for reduce model codes @@ -99,16 +98,14 @@ public static boolean isEmpty(T[] array) { * @param collection * @return */ - public static boolean isEmpty(Collection collection) { + public static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } /**判断map是否为空 - * @param - * @param * @param map * @return */ - public static boolean isEmpty(Map map) { + public static boolean isEmpty(Map map) { return map == null || map.isEmpty(); } //判断是否为空 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -163,20 +160,17 @@ public static int count(T[] array) { return array == null ? 0 : array.length; } /**获取数量 - * @param * @param collection List, Vector, Set等都是Collection的子类 * @return */ - public static int count(Collection collection) { + public static int count(Collection collection) { return collection == null ? 0 : collection.size(); } /**获取数量 - * @param - * @param * @param map * @return */ - public static int count(Map map) { + public static int count(Map map) { return map == null ? 0 : map.size(); } //获取集合长度 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/src/main/java/apijson/framework/ColumnUtil.java b/src/main/java/apijson/framework/ColumnUtil.java new file mode 100644 index 0000000..dcd9432 --- /dev/null +++ b/src/main/java/apijson/framework/ColumnUtil.java @@ -0,0 +1,448 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework; + +import java.util.*; +import java.util.Map.Entry; + +import apijson.RequestMethod; +import apijson.StringUtil; +import apijson.orm.AbstractSQLConfig; +import apijson.orm.AbstractSQLExecutor; + + +/**表字段相关工具类 + * @author Lemon + * @see 先提前配置 {@link #VERSIONED_TABLE_COLUMN_MAP}, {@link #VERSIONED_KEY_COLUMN_MAP} 等,然后调用相关方法。 + * 不支持直接关联 database, schema, datasource,可以把这些与 table 拼接为一个字符串传给参数 table,格式可以是 database-schema-datasource-table + */ +public class ColumnUtil { + + /**带版本的表和字段一对多对应关系,用来做 反选字段 + * Map> + */ + public static SortedMap>> VERSIONED_TABLE_COLUMN_MAP; + + /**带版本的 JSON key 和表字段一对一对应关系,用来做字段名映射 + * Map>> + */ + public static SortedMap>> VERSIONED_KEY_COLUMN_MAP; + + /**带版本的 JSON key 和表字段一对一对应关系,用来做字段名映射,与 VERSIONED_KEY_COLUMN_MAP 相反 + * Map>> + */ + private static SortedMap>> VERSIONED_COLUMN_KEY_MAP; + + public static final Comparator DESC_COMPARATOR = new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + if (o2 == null) { + return o1 == null ? 0 : -1; + } + return o2.compareTo(o1); + } + }; + + static { + VERSIONED_TABLE_COLUMN_MAP = new TreeMap<>(DESC_COMPARATOR); + VERSIONED_KEY_COLUMN_MAP = new TreeMap<>(DESC_COMPARATOR); + VERSIONED_COLUMN_KEY_MAP = new TreeMap<>(DESC_COMPARATOR); + } + + /**初始化 + */ + public static void init() { + VERSIONED_COLUMN_KEY_MAP.clear(); + + // 反过来补全 column -> key 的配置,以空间换时间 + Set>>> set = VERSIONED_KEY_COLUMN_MAP.entrySet(); + if (set != null && set.isEmpty() == false) { + + SortedMap>> map = new TreeMap<>(DESC_COMPARATOR); + + for (Entry>> entry : set) { + + Map> tableKeyColumnMap = entry == null ? null : entry.getValue(); + Set>> tableKeyColumnSet = tableKeyColumnMap == null ? null : tableKeyColumnMap.entrySet(); + + if (tableKeyColumnSet != null && tableKeyColumnSet.isEmpty() == false) { + + Map> tableColumnKeyMap = new HashMap<>(); + + for (Entry> tableKeyColumnEntry : tableKeyColumnSet) { + + Map keyColumnMap = tableKeyColumnEntry == null ? null : tableKeyColumnEntry.getValue(); + Set> keyColumnSet = keyColumnMap == null ? null : keyColumnMap.entrySet(); + + if (keyColumnSet != null && keyColumnSet.isEmpty() == false) { + Map columnKeyMap = new HashMap<>(); + for (Entry keyColumnEntry : keyColumnSet) { + if (keyColumnEntry == null) { + continue; + } + + columnKeyMap.put(keyColumnEntry.getValue(), keyColumnEntry.getKey()); + } + + tableColumnKeyMap.put(tableKeyColumnEntry.getKey(), columnKeyMap); + } + } + + map.put(entry.getKey(), tableColumnKeyMap); + } + } + + VERSIONED_COLUMN_KEY_MAP = map; + } + + + // 补全剩下未定义别名的 key,以空间换时间 + Set>>> allSet = VERSIONED_TABLE_COLUMN_MAP.entrySet(); + if (allSet != null && allSet.isEmpty() == false) { + + for (Entry>> entry : allSet) { + Map> keyColumnMap = VERSIONED_KEY_COLUMN_MAP.get(entry.getKey()); +// 没必要,没特殊配置的就原样返回,没有安全隐患,还能减少性能浪费 Map> columnKeyMap = VERSIONED_COLUMN_KEY_MAP.get(entry.getKey()); + if (keyColumnMap == null) { + keyColumnMap = new LinkedHashMap<>(); + VERSIONED_KEY_COLUMN_MAP.put(entry.getKey(), keyColumnMap); + } +// if (columnKeyMap == null) { +// columnKeyMap = new LinkedHashMap<>(); +// VERSIONED_COLUMN_KEY_MAP.put(entry.getKey(), columnKeyMap); +// } + + Map> tableKeyColumnMap = entry == null ? null : entry.getValue(); + Set>> tableKeyColumnSet = tableKeyColumnMap == null ? null : tableKeyColumnMap.entrySet(); + + if (tableKeyColumnSet != null && tableKeyColumnSet.isEmpty() == false) { + + for (Entry> tableKeyColumnEntry : tableKeyColumnSet) { + + List list = tableKeyColumnEntry == null ? null : tableKeyColumnEntry.getValue(); + + if (list != null && list.isEmpty() == false) { + + Map kcm = keyColumnMap.get(tableKeyColumnEntry.getKey()); +// Map ckm = columnKeyMap.get(tableKeyColumnEntry.getKey()); + if (kcm == null) { + kcm = new LinkedHashMap<>(); + keyColumnMap.put(tableKeyColumnEntry.getKey(), kcm); + } +// if (ckm == null) { +// ckm = new LinkedHashMap<>(); +// columnKeyMap.put(tableKeyColumnEntry.getKey(), ckm); +// } + + for (String column : list) { + if (column == null) { + continue; + } + +// ckm.putIfAbsent(column, column); + //FIXME 对 Comment.toId (多版本) 居然不起作用 +// if (kcm.containsValue(column) == false) { + kcm.putIfAbsent(column, column); +// } + } + +// for (String column : list) { +// if (column == null || ckm.get(column) != null) { +// continue; +// } +// +// kcm.putIfAbsent(column, column); +// } + + } + } + + } + } + + } + + } + + /**适配请求参数 JSON 中 @column:value 的 value 中的 key。支持 !key 反选字段 和 字段名映射 + * @param columns + * @param table + * @param method + * @return + */ + public static List compatInputColumn(List columns, String table, RequestMethod method) { + return compatInputColumn(columns, table, method, null, false); + } + + /**适配请求参数 JSON 中 @column:value 的 value 中的 key。支持 !key 反选字段 和 字段名映射 + * @param columns + * @param table + * @param method + * @param version + * @return + * @see 先提前配置 {@link #VERSIONED_TABLE_COLUMN_MAP},然后在 {@link AbstractSQLConfig} 的子类重写 {@link AbstractSQLConfig#setColumn } 并调用这个方法,例如 + *
+	public AbstractSQLConfig setColumn(List column) { 
+ return super.setColumn(ColumnUtil.compatInputColumn(column, getTable(), version));
+ } + *
+ */ + public static List compatInputColumn(List columns, String table, RequestMethod method, Integer version, boolean throwWhenNoKey) { + String[] keys = columns == null ? null : columns.toArray(new String[]{}); // StringUtil.split(c, ";"); + if (keys == null || keys.length <= 0) { // JOIN 副表可以设置 @column:"" 来指定不返回字段 + return columns != null ? columns : getClosestValue(VERSIONED_TABLE_COLUMN_MAP, version, table); + } + + // boolean isQueryMethod = RequestMethod.isQueryMethod(method); + + List exceptColumns = new ArrayList<>(); // Map exceptColumnMap = new HashMap<>(); + List newColumns = new ArrayList<>(); + + Map keyColumnMap = getClosestValue(VERSIONED_KEY_COLUMN_MAP, version, table); + boolean isEmpty = keyColumnMap == null || keyColumnMap.isEmpty(); + + String q = "`"; + + String expression; + //...;fun0(arg0,arg1,...):fun0;fun1(arg0,arg1,...):fun1;... + for (int i = 0; i < keys.length; i++) { + + //!column,column2,!column3,column4:alias4;fun(arg0,arg1,...) + expression = keys[i]; + int start = expression.indexOf("("); + int end = expression.lastIndexOf(")"); + if (start >= 0 && start < end) { + String[] ks = StringUtil.split(expression.substring(start + 1, end)); + + String expr = expression.substring(0, start + 1); + for (int j = 0; j < ks.length; j++) { + String ck = ks[j]; + boolean hasQuote = false; + if (ck.endsWith("`")) { + String nck = ck.substring(0, ck.length() - 1); + if (nck.lastIndexOf("`") == 0) { + ck = nck.substring(1); + hasQuote = true; + } + } + + String rc = null; + if (hasQuote || StringUtil.isName(ck)) { + rc = isEmpty ? null : keyColumnMap.get(ck); + if (rc == null && isEmpty == false && throwWhenNoKey) { + throw new NullPointerException(table + ":{ @column: value } 的 value 中 " + ck + " 不合法!不允许传后端未授权访问的字段名!"); + } + } + + expr += (j <= 0 ? "" : ",") + (hasQuote ? q : "") + (rc == null ? ck : rc) + (hasQuote ? q : ""); + } + + newColumns.add(expr + expression.substring(end)); + +// newColumns.add(expression); + continue; + } + + String[] ckeys = StringUtil.split(expression); + if (ckeys != null && ckeys.length > 0) { + for (int j = 0; j < ckeys.length; j++) { + String ck = ckeys[j]; + + if (ck.startsWith("!")) { + if (ck.length() <= 1) { + throw new IllegalArgumentException("@column:value 的 value 中 " + ck + + " 不合法! !column 不允许 column 为空字符串!column,!column2,!column3,column4:alias4 中所有 column 必须符合变量名格式!"); + } + String c = ck.substring(1); + if (StringUtil.isName(c) == false) { + throw new IllegalArgumentException("@column:value 的 value 中 " + c + + " 不合法! column,!column2,!column3,column4:alias4 中所有 column 必须符合变量名格式!"); + } + + String rc = isEmpty ? null : keyColumnMap.get(c); + exceptColumns.add(rc == null ? c : rc); // 不使用数据库别名,以免 JOIN 等复杂查询情况下报错字段不存在 exceptColumnMap.put(nc == null ? c : nc, c); // column:alias + } else { + boolean hasQuote = false; + if (ck.endsWith("`")) { + String nck = ck.substring(0, ck.length() - 1); + if (nck.lastIndexOf("`") == 0) { + ck = nck.substring(1); + hasQuote = true; + } + } + + String rc = null; + if (hasQuote || StringUtil.isName(ck)) { + rc = isEmpty ? null : keyColumnMap.get(ck); + if (rc == null && isEmpty == false && throwWhenNoKey) { + throw new NullPointerException(table + ":{ @column: value } 的 value 中 " + ck + " 不合法!不允许传后端未授权访问的字段名!"); + } + } + + newColumns.add(rc == null ? ck : rc); // 不使用数据库别名,以免 JOIN 等复杂查询情况下报错字段不存在 newColumns.add(rc == null ? ck : (isQueryMethod ? (rc + ":" + ck) : rc)); + } + } + } + } + + List allColumns = exceptColumns == null || exceptColumns.isEmpty() ? null : getClosestValue(VERSIONED_TABLE_COLUMN_MAP, version, table); + + if (allColumns != null && allColumns.isEmpty() == false) { + + // 不使用数据库别名,以免 JOIN 等复杂查询情况下报错字段不存在 + // Map> tableColumnKeyMap = VERSIONED_COLUMN_KEY_MAP == null || VERSIONED_COLUMN_KEY_MAP.isEmpty() ? null : VERSIONED_COLUMN_KEY_MAP.get(version); + // Map columnKeyMap = tableColumnKeyMap == null || tableColumnKeyMap.isEmpty() ? null : tableColumnKeyMap.get(table); + + for (String c : allColumns) { + if (c != null && exceptColumns.contains(c) == false) { // column:alias + // 不使用数据库别名,以免 JOIN 等复杂查询情况下报错字段不存在 String alias = isQueryMethod == false || columnKeyMap == null || columnKeyMap.isEmpty() ? null : columnKeyMap.get(c); + newColumns.add(c); // newColumns.add(alias == null ? c : (c + ":" + alias)); + } + } + } + + return newColumns; + } + + + /**适配请求参数 JSON 中 条件/赋值 键值对的 key + * @param key + * @param table + * @param method + * @return + */ + public static String compatInputKey(String key, String table, RequestMethod method) { + return compatInputKey(key, table, method, null, false); + } + + /**适配请求参数 JSON 中 条件/赋值 键值对的 key + * @param key + * @param table + * @param method + * @param version + * @return + * @see 先提前配置 {@link #VERSIONED_KEY_COLUMN_MAP},然后在 {@link AbstractSQLConfig} 的子类重写 {@link AbstractSQLConfig#getKey } 并调用这个方法,例如 + *
+	public String getKey(String key) { 
+ return super.getKey(ColumnUtil.compatInputKey(key, getTable(), version));
+ } + *
+ */ + public static String compatInputKey(String key, String table, RequestMethod method, Integer version, boolean throwWhenNoKey) { + Map keyColumnMap = getClosestValue(VERSIONED_KEY_COLUMN_MAP, version, table); + boolean isEmpty = keyColumnMap == null || keyColumnMap.isEmpty(); + String alias = isEmpty ? null : keyColumnMap.get(key); + if (alias == null) { + if (isEmpty == false && throwWhenNoKey) { + throw new NullPointerException(table + ":{} 中不允许传 " + key + " !"); + } + return key; + } + + return alias; + } + + /**适配返回结果 JSON 中键值对的 key。可能通过不传 @column 等方式来返回原始字段名,这样就达不到隐藏真实字段名的需求了,所以只有最终这个兜底方式靠谱。 + * @param key + * @param table + * @param method + * @return + */ + public static String compatOutputKey(String key, String table, RequestMethod method) { + return compatOutputKey(key, table, method, null); + } + + /**适配返回结果 JSON 中键值对的 key。可能通过不传 @column 等方式来返回原始字段名,这样就达不到隐藏真实字段名的需求了,所以只有最终这个兜底方式靠谱。 + * @param key + * @param table + * @param method + * @param version + * @return + * @see 先提前配置 {@link #VERSIONED_COLUMN_KEY_MAP},然后在 {@link AbstractSQLExecutor} 的子类重写 {@link AbstractSQLExecutor#getKey } 并调用这个方法,例如 + *
+	protected String getKey(SQLConfig config, ResultSet rs, ResultSetMetaData rsmd, int tablePosition, JSONMap table,
+	int columnIndex, Map childMap) throws Exception { 
+ return ColumnUtil.compatOutputKey(super.getKey(config, rs, rsmd, tablePosition, table, columnIndex, childMap), config.getTable(), config.getMethod(), version);
+ } + *
+ */ + public static String compatOutputKey(String key, String table, RequestMethod method, Integer version) { + Map columnKeyMap = getClosestValue(VERSIONED_COLUMN_KEY_MAP, version, table); + String alias = columnKeyMap == null || columnKeyMap.isEmpty() ? null : columnKeyMap.get(key); + return alias == null ? key : alias; + } + + public static T getClosestValue(SortedMap> versionedMap, Integer version, String table) { + boolean isEmpty = versionedMap == null || versionedMap.isEmpty(); + + Map map = isEmpty || version == null ? null : versionedMap.get(version); + T m = map == null ? null : map.get(table); + if (isEmpty == false && m == null) { + Set>> set = versionedMap.entrySet(); + + T lm = null; + for (Entry> entry : set) { + Map val = entry.getValue(); + m = val == null ? null : val.get(table); + if (m == null) { + continue; + } + + if (version == null || version == 0) { + // versionedMap.put(null, val); + return m; + } + + Integer key = entry.getKey(); + if (key == null) { + lm = m; + map = val; + continue; + } + + if (version >= key) { + versionedMap.put(version, val); + return m; + } + + break; + } + + if (lm != null) { + m = lm; + } + + if (map != null) { + versionedMap.put(version, map); + } + } + + return m; + } + + + /**把多个表名相关属性拼接成一个表名 + * @param database + * @param schema + * @param datasource + * @param table + * @return + */ + public static String concat(String database, String schema, String datasource, String table) { + return database + "-" + schema + "-" + datasource + "-" + table; + } + + +} diff --git a/src/main/java/apijson/framework/javax/APIJSONApplication.java b/src/main/java/apijson/framework/javax/APIJSONApplication.java new file mode 100644 index 0000000..b9e0352 --- /dev/null +++ b/src/main/java/apijson/framework/javax/APIJSONApplication.java @@ -0,0 +1,199 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +import apijson.Log; +import apijson.NotNull; +import apijson.orm.AbstractFunctionParser; +import apijson.orm.script.ScriptExecutor; + +import java.rmi.ServerException; +import java.util.List; +import java.util.Map; + + +/**启动入口 Application + * 右键这个类 > Run As > Java Application + * @author Lemon + */ +public class APIJSONApplication { + public static final String TAG = "APIJSONApplication"; + + @NotNull + public static APIJSONCreator, ? extends List> DEFAULT_APIJSON_CREATOR; + static { + DEFAULT_APIJSON_CREATOR = new APIJSONCreator<>(); + } + + @SuppressWarnings("unchecked") + public static , L extends List> APIJSONParser createParser() { + return (APIJSONParser) DEFAULT_APIJSON_CREATOR.createParser(); + } + @SuppressWarnings("unchecked") + public static , L extends List> APIJSONFunctionParser createFunctionParser() { + return (APIJSONFunctionParser) DEFAULT_APIJSON_CREATOR.createFunctionParser(); + } + + @SuppressWarnings("unchecked") + public static , L extends List> APIJSONVerifier createVerifier() { + return (APIJSONVerifier) DEFAULT_APIJSON_CREATOR.createVerifier(); + } + + @SuppressWarnings("unchecked") + public static , L extends List> APIJSONSQLConfig createSQLConfig() { + return (APIJSONSQLConfig) DEFAULT_APIJSON_CREATOR.createSQLConfig(); + } + + @SuppressWarnings("unchecked") + public static , L extends List> APIJSONSQLExecutor createSQLExecutor() { + return (APIJSONSQLExecutor) DEFAULT_APIJSON_CREATOR.createSQLExecutor(); + } + + + /**初始化,加载所有配置并校验 + * @return + * @throws Exception + */ + public static void init() throws Exception { + init(true, DEFAULT_APIJSON_CREATOR); + } + /**初始化,加载所有配置并校验 + * @param shutdownWhenServerError + * @return + * @throws Exception + */ + public static void init(boolean shutdownWhenServerError) throws Exception { + init(shutdownWhenServerError, DEFAULT_APIJSON_CREATOR); + } + /**初始化,加载所有配置并校验 + * @param creator + * @return + * @throws Exception + */ + public static , L extends List> void init( + @NotNull APIJSONCreator creator) throws Exception { + init(true, creator); + } + /**初始化,加载所有配置并校验 + * @param shutdownWhenServerError + * @param creator + * @return + * @throws Exception + */ + public static , L extends List> void init( + boolean shutdownWhenServerError, @NotNull APIJSONCreator creator) throws Exception { + System.out.println("\n\n\n\n\n<<<<<<<<<<<<<<<<<<<<<<<<< APIJSON 开始启动 >>>>>>>>>>>>>>>>>>>>>>>>\n"); + DEFAULT_APIJSON_CREATOR = creator; + + if (APIJSONVerifier.ENABLE_VERIFY_ROLE) { + System.out.println("\n\n\n开始初始化: Access 权限校验配置 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + try { + APIJSONVerifier.initAccess(shutdownWhenServerError, creator); + } catch (Throwable e) { + e.printStackTrace(); + if (shutdownWhenServerError) { + onServerError("Access 权限校验配置 初始化失败!", shutdownWhenServerError); + } + } + System.out.println("\n完成初始化: Access 权限校验配置 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + } + + + if (APIJSONFunctionParser.ENABLE_REMOTE_FUNCTION) { + System.out.println("\n\n\n开始初始化: Function 远程函数配置 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + try { + APIJSONFunctionParser.init(shutdownWhenServerError, creator); + } catch (Throwable e) { + e.printStackTrace(); + if (shutdownWhenServerError) { + onServerError("Function 远程函数配置 初始化失败!", shutdownWhenServerError); + } + } + System.out.println("\n完成初始化: Function 远程函数配置 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + + System.out.println("开始测试: Function 远程函数 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + try { + APIJSONFunctionParser.test(); + } catch (Throwable e) { + e.printStackTrace(); + if (shutdownWhenServerError) { + onServerError("Function 远程函数配置 测试失败!", shutdownWhenServerError); + } + } + System.out.println("\n完成测试: Function 远程函数 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + } + + + if (APIJSONVerifier.ENABLE_VERIFY_CONTENT) { + System.out.println("\n\n\n开始初始化: Request 请求参数校验配置 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + try { + APIJSONVerifier.initRequest(shutdownWhenServerError, creator); + } catch (Throwable e) { + e.printStackTrace(); + if (shutdownWhenServerError) { + onServerError("Request 请求参数校验配置 初始化失败!", shutdownWhenServerError); + } + } + System.out.println("\n完成初始化: Request 请求参数校验校验配置 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + + System.out.println("\n\n\n开始测试: Request 请求参数校验 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + try { + APIJSONVerifier.testStructure(); + } catch (Throwable e) { + e.printStackTrace(); + if (shutdownWhenServerError) { + onServerError("Request 请求参数校验 测试失败!", shutdownWhenServerError); + } + } + System.out.println("\n完成测试: Request 请求参数校验 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + } + + if (APIJSONVerifier.ENABLE_APIJSON_ROUTER) { + System.out.println("\n\n\n开始初始化: Document 请求映射配置 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + try { + APIJSONVerifier.initDocument(shutdownWhenServerError, creator); + } catch (Throwable e) { + e.printStackTrace(); + if (shutdownWhenServerError) { + onServerError("Document 请求映射配置 初始化失败!", shutdownWhenServerError); + } + } + System.out.println("\n完成初始化: Document 请求映射配置 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + } + + + System.out.println("官方网站: http://apijson.cn"); + System.out.println("设计规范: https://github.com/Tencent/APIJSON/blob/master/Document.md#3"); + System.out.println("测试链接: http://apijson.cn/api?type=JSON&url=http://localhost:8080/get"); + System.out.println("\n\n<<<<<<<<<<<<<<<<<<<<<<<<< APIJSON 启动完成,试试调用零代码万能通用 API 吧 ^_^ >>>>>>>>>>>>>>>>>>>>>>>>\n"); + } + + protected static void onServerError(String msg, boolean shutdown) throws ServerException { + Log.e(TAG, "\n启动时自检测试未通过!原因:\n" + msg); + + if (shutdown) { + System.exit(1); + } else { + throw new ServerException(msg); + } + } + + public static void addScriptExecutor(String language, ScriptExecutor scriptExecutor) { + scriptExecutor.init(); + AbstractFunctionParser.SCRIPT_EXECUTOR_MAP.put(language, scriptExecutor); + } + + +} diff --git a/src/main/java/apijson/framework/javax/APIJSONConstant.java b/src/main/java/apijson/framework/javax/APIJSONConstant.java new file mode 100644 index 0000000..87a2a37 --- /dev/null +++ b/src/main/java/apijson/framework/javax/APIJSONConstant.java @@ -0,0 +1,20 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +/**APIJSON 常量类 + * @author Lemon + */ +public class APIJSONConstant extends apijson.framework.APIJSONConstant {} diff --git a/src/main/java/apijson/framework/javax/APIJSONController.java b/src/main/java/apijson/framework/javax/APIJSONController.java new file mode 100755 index 0000000..d2d8e7f --- /dev/null +++ b/src/main/java/apijson/framework/javax/APIJSONController.java @@ -0,0 +1,817 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +import apijson.*; +import apijson.JSONRequest; +import apijson.orm.*; + +import javax.servlet.http.HttpSession; + +import java.rmi.ServerException; +import java.util.*; + +import static apijson.JSON.*; +import static apijson.RequestMethod.*; +import static apijson.framework.javax.APIJSONConstant.*; + + +/**APIJSON base controller,建议在子项目被 @RestController 注解的类继承它或通过它的实例调用相关方法 + *
全通过 HTTP POST 来请求: + *
1.减少代码 - 客户端无需写 HTTP GET, HTTP PUT 等各种方式的请求代码 + *
2.提高性能 - 无需 URL encode 和 decode + *
3.调试方便 - 建议使用 APIAuto-机器学习自动化接口管理工具(https://github.com/TommyLemon/APIAuto) + * @author Lemon + */ +public class APIJSONController, L extends List> { + public static final String TAG = "APIJSONController"; + + public String getRequestURL() { + return null; + } + + public APIJSONParser newParser(HttpSession session, RequestMethod method) { + APIJSONParser parser = APIJSONApplication.createParser(); + parser.setMethod(method); + parser.setSession(session); + parser.setRequestURL(getRequestURL()); + return parser; + } + + public static APIJSONParser, ? extends List> COMMON_PARSER = APIJSONApplication.createParser(); + + /**新建带状态内容的JSONObject + * @param code + * @param msg + * @return + */ + public static > M newResult(int code, String msg) { + return newResult(code, msg, null); + } + + /** + * 添加JSONObject的状态内容,一般用于错误提示结果 + * + * @param code + * @param msg + * @param warn + * @return + */ + public static > M newResult(int code, String msg, String warn) { + return newResult(code, msg, warn, false); + } + + /** + * 新建带状态内容的JSONObject + * + * @param code + * @param msg + * @param warn + * @param isRoot + * @return + */ + public static > M newResult(int code, String msg, String warn, boolean isRoot) { + return extendResult(null, code, msg, warn, isRoot); + } + + /** + * 添加JSONObject的状态内容,一般用于错误提示结果 + * + * @param object + * @param code + * @param msg + * @return + */ + public static > M extendResult(M object, int code, String msg, String warn, boolean isRoot) { + return (M) COMMON_PARSER.extendResult(JSON.createJSONObject(object), code, msg, warn, isRoot); + } + + + /** + * 添加请求成功的状态内容 + * + * @param object + * @return + */ + public M extendSuccessResult(M object) { + return extendSuccessResult(object, false); + } + + public M extendSuccessResult(M object, boolean isRoot) { + return extendSuccessResult(object, null, isRoot); + } + + /**添加请求成功的状态内容 + * @param object + * @param isRoot + * @return + */ + public static > M extendSuccessResult(M object, String warn, boolean isRoot) { + return extendResult(object, JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED, warn, isRoot); + } + + /**获取请求成功的状态内容 + * @return + */ + public static > M newSuccessResult() { + return newSuccessResult(null); + } + + /**获取请求成功的状态内容 + * @param warn + * @return + */ + public static > M newSuccessResult(String warn) { + return newSuccessResult(warn, false); + } + + /**获取请求成功的状态内容 + * @param warn + * @param isRoot + * @return + */ + public static > M newSuccessResult(String warn, boolean isRoot) { + return newResult(JSONResponse.CODE_SUCCESS, JSONResponse.MSG_SUCCEED, warn, isRoot); + } + + /**添加请求成功的状态内容 + * @param object + * @param e + * @return + */ + public static > M extendErrorResult(M object, Throwable e) { + return extendErrorResult(object, e, false); + } + /**添加请求成功的状态内容 + * @param object + * @param e + * @param isRoot + * @return + */ + public static > M extendErrorResult(M object, Throwable e, boolean isRoot) { + return extendErrorResult(object, e, null, null, isRoot); + } + /**添加请求成功的状态内容 + * @param object + * @return + */ + public static > M extendErrorResult(M object, Throwable e, RequestMethod requestMethod, String url, boolean isRoot) { + return (M) COMMON_PARSER.extendErrorResult(JSON.createJSONObject(object), e, requestMethod, url, isRoot); + } + + public static > M newErrorResult(Exception e) { + return newErrorResult(e, false); + } + public static > M newErrorResult(Exception e, boolean isRoot) { + return (M) COMMON_PARSER.newErrorResult(e, isRoot); + } + + + public String parse(RequestMethod method, String request, HttpSession session) { + if (APIJSONVerifier.ENABLE_APIJSON_ROUTER && ! Log.DEBUG) { + return JSON.toJSONString( + newErrorResult( + new IllegalArgumentException("APIJSONVerifier.ENABLE_APIJSON_ROUTER = true 已启用 router," + + "Log.DEBUG = false 时不允许调用 /router/{method}/{tag} 外的万能通用接口!" + ) + ) + ); + } + + return newParser(session, method).parse(request); + } + + public String parseByTag(RequestMethod method, String tag, Map params, String request, HttpSession session) { + if (APIJSONVerifier.ENABLE_APIJSON_ROUTER && ! Log.DEBUG) { + return JSON.toJSONString( + newErrorResult( + new IllegalArgumentException("APIJSONVerifier.ENABLE_APIJSON_ROUTER = true 已启用 router," + + "Log.DEBUG = false 时不允许调用 /router/{method}/{tag} 外的万能通用接口!" + ) + ) + ); + } + + APIJSONParser parser = newParser(session, method); + M req = parser.wrapRequest(method, tag, JSON.parseObject(request), false); + if (req == null) { + req = JSON.createJSONObject(); + } + if (params != null && params.isEmpty() == false) { + req.putAll(params); + } + + return parser.parse(req); + } + + //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + /**全能增删改查统一入口,这个一个方法可替代以下所有万能通用方法,一个接口通用增删改查 + * @param request + * @param session + * @return + */ + public String crudAll(String request, HttpSession session) { + return parse(CRUD, request, session); + } + + /**增删改查统一入口,这个一个方法可替代以下 7 个方法,牺牲一点路由解析性能来提升一些开发效率 + * @param method + * @param request + * @param session + * @return + */ + public String crud(String method, String request, HttpSession session) { + if (METHODS.contains(method)) { + return parse(RequestMethod.valueOf(method.toUpperCase()), request, session); + } + + return toJSONString(newErrorResult( + new IllegalArgumentException("URL 路径 /{method} 中 method 值 " + + method + " 错误!只允许 " + METHODS + " 中的一个!") + )); + } + + /**获取 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#GET} + */ + public String get(String request, HttpSession session) { + return parse(GET, request, session); + } + + /**计数 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#HEAD} + */ + public String head(String request, HttpSession session) { + return parse(HEAD, request, session); + } + + /**限制性GET,request和response都非明文,浏览器看不到,用于对安全性要求高的GET请求 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#GETS} + */ + public String gets(String request, HttpSession session) { + return parse(GETS, request, session); + } + + /**限制性HEAD,request和response都非明文,浏览器看不到,用于对安全性要求高的HEAD请求 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#HEADS} + */ + public String heads(String request, HttpSession session) { + return parse(HEADS, request, session); + } + + /**新增 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#POST} + */ + public String post(String request, HttpSession session) { + return parse(POST, request, session); + } + + /**修改 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#PUT} + */ + public String put(String request, HttpSession session) { + return parse(PUT, request, session); + } + + /**删除 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#DELETE} + */ + public String delete(String request, HttpSession session) { + return parse(DELETE, request, session); + } + + /**支持全局事物、批量执行多条语句 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#GET} + */ + public String crud(String request, HttpSession session) { + return parse(CRUD, request, session); + } + + //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + + //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + /**增删改查统一入口,这个一个方法可替代以下 7 个方法,牺牲一些路由解析性能来提升一点开发效率 + * @param method + * @param tag + * @param params + * @param request + * @param session + * @return + */ + public String crudByTag(String method, String tag, Map params, String request, HttpSession session) { + if (METHODS.contains(method)) { + return parseByTag(RequestMethod.valueOf(method.toUpperCase()), tag, params, request, session); + } + + return toJSONString(newErrorResult( + new IllegalArgumentException("URL 路径 /{method}/{tag} 中 method 值 " + + method + " 错误!只允许 " + METHODS + " 中的一个!") + )); + } + + +// /**获取列表 +// * @param request 只用String,避免encode后未decode +// * @param session +// * @return +// * @see {@link RequestMethod#GET} +// */ +// public String listByTag(String tag, String request, HttpSession session) { +// return parseByTag(GET, tag + apijson.JSONMap.KEY_ARRAY, request, session); +// } + + /**获取 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#GET} + */ + public String getByTag(String tag, Map params, String request, HttpSession session) { + return parseByTag(GET, tag, params, request, session); + } + + + /**计数 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#HEAD} + */ + public String headByTag(String tag, Map params, String request, HttpSession session) { + return parseByTag(HEAD, tag, params, request, session); + } + + /**限制性GET,request和response都非明文,浏览器看不到,用于对安全性要求高的GET请求 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#GETS} + */ + public String getsByTag(String tag, Map params, String request, HttpSession session) { + return parseByTag(GETS, tag, params, request, session); + } + + /**限制性HEAD,request和response都非明文,浏览器看不到,用于对安全性要求高的HEAD请求 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#HEADS} + */ + public String headsByTag(String tag, Map params, String request, HttpSession session) { + return parseByTag(HEADS, tag, params, request, session); + } + + /**新增 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#POST} + */ + public String postByTag(String tag, Map params, String request, HttpSession session) { + return parseByTag(POST, tag, params, request, session); + } + + /**修改 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#PUT} + */ + public String putByTag(String tag, Map params, String request, HttpSession session) { + return parseByTag(PUT, tag, params, request, session); + } + + /**删除 + * @param request 只用String,避免encode后未decode + * @param session + * @return + * @see {@link RequestMethod#DELETE} + */ + public String deleteByTag(String tag, Map params, String request, HttpSession session) { + return parseByTag(DELETE, tag, params, request, session); + } + //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + /**增删改查统一的类 RESTful API 入口,牺牲一些路由解析性能来提升一点开发效率 + * compatCommonAPI = Log.DEBUG + * @param method + * @param tag + * @param params + * @param request + * @param session + * @return + */ + public String router(String method, String tag, Map params, String request, HttpSession session) { + return router(method, tag, params, request, session, Log.DEBUG); + } + /**增删改查统一的类 RESTful API 入口,牺牲一些路由解析性能来提升一点开发效率 + * @param method + * @param tag + * @param params + * @param request + * @param session + * @param compatCommonAPI 兼容万能通用 API,当没有映射 APIJSON 格式请求时,自动转到万能通用 API + * @return + */ + public String router(String method, String tag, Map params, String request, HttpSession session, boolean compatCommonAPI) { + if (! APIJSONVerifier.ENABLE_APIJSON_ROUTER) { + return JSON.toJSONString( + newErrorResult( + new IllegalArgumentException("未启用 router!请配置 APIJSONVerifier.ENABLE_APIJSON_ROUTER = true !" + ) + ) + ); + } + + RequestMethod requestMethod = null; + try { + requestMethod = RequestMethod.valueOf(method.toUpperCase()); + } catch (Throwable e) { + // 下方 METHODS.contains(method) 会抛异常 + } + Parser parser = newParser(session, requestMethod); + + if (METHODS.contains(method) == false) { + return JSON.toJSONString( + newErrorResult( + new IllegalArgumentException("URL 路径 /{method}/{tag} 中 method 值 " + + method + " 错误!只允许 " + METHODS + " 中的一个!" + ) + ) + ); + } + + String t = compatCommonAPI && tag != null && tag.endsWith("[]") ? tag.substring(0, tag.length() - 2) : tag; + if (StringUtil.isName(t) == false) { + return JSON.toJSONString( + newErrorResult( + new IllegalArgumentException("URL 路径 /" + method + "/{tag} 的 tag 中 " + + t + " 错误!tag 不能为空,且只允许变量命名格式!" + ) + ) + ); + } + + String versionStr = params == null ? null : params.remove(APIJSONConstant.VERSION); + Integer version; + try { + version = StringUtil.isEmpty(versionStr, false) ? null : Integer.valueOf(versionStr); + } + catch (Exception e) { + return JSON.toJSONString( + newErrorResult(new IllegalArgumentException("URL 路径 /" + method + "/" + + tag + "?version=value 中 value 值 " + versionStr + " 错误!必须符合整数格式!") + ) + ); + } + + if (version == null) { + version = 0; + } + + try { + // 从 Document 查这样的接口 + String cacheKey = AbstractVerifier.getCacheKeyForRequest(method, tag); + SortedMap> versionedMap = APIJSONVerifier.DOCUMENT_MAP.get(cacheKey); + + Map result = versionedMap == null ? null : versionedMap.get(version); + if (result == null) { // version <= 0 时使用最新,version > 0 时使用 > version 的最接近版本(最小版本) + Set>> set = versionedMap == null ? null : versionedMap.entrySet(); + + if (set != null && set.isEmpty() == false) { + Map.Entry> maxEntry = null; + + for (Map.Entry> entry : set) { + if (entry == null || entry.getKey() == null || entry.getValue() == null) { + continue; + } + + if (version == null || version <= 0 || version == entry.getKey()) { // 这里应该不会出现相等,因为上面 versionedMap.get(Integer.valueOf(version)) + maxEntry = entry; + break; + } + + if (entry.getKey() < version) { + break; + } + + maxEntry = entry; + } + + result = maxEntry == null ? null : maxEntry.getValue(); + } + + if (result != null) { // 加快下次查询,查到值的话组合情况其实是有限的,不属于恶意请求 + if (versionedMap == null) { + versionedMap = new TreeMap<>((o1, o2) -> { + return o2 == null ? -1 : o2.compareTo(o1); // 降序 + }); + } + + versionedMap.put(version, result); + APIJSONVerifier.DOCUMENT_MAP.put(cacheKey, versionedMap); + } + } + + @SuppressWarnings("unchecked") + APIJSONCreator creator = (APIJSONCreator) APIJSONApplication.DEFAULT_APIJSON_CREATOR; + if (result == null && Log.DEBUG && APIJSONVerifier.DOCUMENT_MAP.isEmpty()) { + + //获取指定的JSON结构 <<<<<<<<<<<<<< + SQLConfig config = creator.createSQLConfig().setMethod(GET).setTable(APIJSONConstant.DOCUMENT_); + config.setPrepared(false); + config.setColumn(Arrays.asList("request,apijson")); + + Map where = new HashMap(); + where.put("url", "/" + method + "/" + tag); + where.put("apijson{}", "length(apijson)>0"); + + if (version > 0) { + where.put(JSONRequest.KEY_VERSION + ">=", version); + } + config.setWhere(where); + config.setOrder(JSONRequest.KEY_VERSION + (version > 0 ? "+" : "-")); + config.setCount(1); + + //too many connections error: 不try-catch,可以让客户端看到是服务器内部异常 + result = creator.createSQLExecutor().execute(config, false); + + // version, method, tag 组合情况太多了,JDK 里又没有 LRUCache,所以要么启动时一次性缓存全部后面只用缓存,要么每次都查数据库 + // versionedMap.put(Integer.valueOf(version), result); + // DOCUMENT_MAP.put(cacheKey, versionedMap); + } + + String apijson = result == null ? null : getString(result, "apijson"); + if (StringUtil.isEmpty(apijson, true)) { // + if (compatCommonAPI) { + return crudByTag(method, tag, params, request, session); + } + + throw new IllegalArgumentException("URL 路径 /" + method + + "/" + tag + (versionStr == null ? "" : "?version=" + versionStr) + " 对应的接口不存在!"); + } + + M rawReq = JSON.parseObject(request); + if (rawReq == null) { + rawReq = JSON.createJSONObject(); + } + if (params != null && params.isEmpty() == false) { + rawReq.putAll(params); + } + + if (parser.isNeedVerifyContent()) { + Verifier verifier = parser.getVerifier(); + + //获取指定的JSON结构 <<<<<<<<<<<< + Map target = parser.getStructure("Request", method.toUpperCase(), tag, version); + if (target == null) { //empty表示随意操作 || object.isEmpty()) { + throw new UnsupportedOperationException("找不到 version: " + version + ", method: " + method.toUpperCase() + ", tag: " + tag + " 对应的 structure !" + + "非开放请求必须是后端 Request 表中校验规则允许的操作!如果需要则在 Request 表中新增配置!"); + } + + //M clone 浅拷贝没用,Structure.parse 会导致 structure 里面被清空,第二次从缓存里取到的就是 {} + verifier.verifyRequest(requestMethod, "", JSON.createJSONObject(target), rawReq, 0, null, null); + } + + M apijsonReq = JSON.parseObject(apijson); + if (apijsonReq == null) { + apijsonReq = JSON.createJSONObject(); + } + + Set> rawSet = rawReq.entrySet(); + if (rawSet != null && rawSet.isEmpty() == false) { + for (Map.Entry entry : rawSet) { + String key = entry == null ? null : entry.getKey(); + if (key == null) { // value 为 null 有效 + continue; + } + + String[] pathKeys = key.split("\\."); + //逐层到达child的直接容器JSONObject parent + int last = pathKeys.length - 1; + M parent = apijsonReq; + for (int i = 0; i < last; i++) {//一步一步到达指定位置 + M p = getJSONObject(parent, pathKeys[i]); + if (p == null) { + p = JSON.createJSONObject(); + parent.put(key, p); + } + parent = p; + } + + parent.put(pathKeys[last], entry.getValue()); + } + } + + // 没必要,已经是预设好的实际参数了,如果要 tag 就在 apijson 字段配置 apijsonReq.put(JSONRequest.KEY_TAG, tag); + + return parser.setNeedVerifyContent(false).parse(apijsonReq); + } + catch (Exception e) { + return JSON.toJSONString(newErrorResult(e)); + } + } + + //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + + + + /**重新加载配置 + * @return + * @see + *
+	{
+	"type": "ALL",  //重载对象,ALL, FUNCTION, REQUEST, ACCESS,非必须
+	"phone": "13000082001",
+	"verify": "1234567" //验证码,对应类型为 Verify.TYPE_RELOAD
+	}
+	 * 
+ */ + public M reload(String type) { + M result = newSuccessResult(); + + boolean reloadAll = StringUtil.isEmpty(type, true) || "ALL".equals(type); + + if (reloadAll || "ACCESS".equals(type)) { + try { + if (reloadAll == false && APIJSONVerifier.ENABLE_VERIFY_ROLE == false) { + throw new UnsupportedOperationException("AbstractVerifier.ENABLE_VERIFY_ROLE == false 时不支持校验角色权限!" + + "如需支持则设置 AbstractVerifier.ENABLE_VERIFY_ROLE = true !"); + } + + if (APIJSONVerifier.ENABLE_VERIFY_ROLE) { + result.put(ACCESS_, APIJSONVerifier.initAccess()); + } + } catch (ServerException e) { + e.printStackTrace(); + result.put(ACCESS_, newErrorResult(e)); + } + } + + if (reloadAll || "FUNCTION".equals(type)) { + try { + if (reloadAll == false && APIJSONFunctionParser.ENABLE_REMOTE_FUNCTION == false) { + throw new UnsupportedOperationException("AbstractFunctionParser.ENABLE_REMOTE_FUNCTION" + + " == false 时不支持远程函数!如需支持则设置 AbstractFunctionParser.ENABLE_REMOTE_FUNCTION = true !"); + } + + if (APIJSONFunctionParser.ENABLE_REMOTE_FUNCTION) { + result.put(FUNCTION_, APIJSONFunctionParser.init()); + } + } catch (ServerException e) { + e.printStackTrace(); + result.put(FUNCTION_, newErrorResult(e)); + } + } + + if (reloadAll || "REQUEST".equals(type)) { + try { + if (reloadAll == false && APIJSONVerifier.ENABLE_VERIFY_CONTENT == false) { + throw new UnsupportedOperationException("AbstractVerifier.ENABLE_VERIFY_CONTENT == false 时不支持校验请求传参内容!" + + "如需支持则设置 AbstractVerifier.ENABLE_VERIFY_CONTENT = true !"); + } + + if (APIJSONVerifier.ENABLE_VERIFY_CONTENT) { + result.put(REQUEST_, APIJSONVerifier.initRequest()); + } + } catch (ServerException e) { + e.printStackTrace(); + result.put(REQUEST_, newErrorResult(e)); + } + } + + return result; + } + + + /**用户登录 + * @param session + * @param visitor + * @param version + * @param format + * @param defaults + * @return 返回类型设置为 Object 是为了子类重写时可以有返回值,避免因为冲突而另写一个有返回值的登录方法 + */ + public Object login(@NotNull HttpSession session, @NotNull Visitor visitor, Integer version, Boolean format, M defaults) { + //登录状态保存至session + session.setAttribute(VISITOR_ID, visitor.getId()); //用户id + session.setAttribute(VISITOR_, visitor); //用户 + session.setAttribute(VERSION, version); //全局默认版本号 + session.setAttribute(FORMAT, format); //全局默认格式化配置 + session.setAttribute(DEFAULTS, defaults); //给每个请求JSON最外层加的字段 + return null; + } + + /**退出登录,清空session + * @param session + * @return 返回类型设置为 Object 是为了子类重写时可以有返回值,避免因为冲突而另写一个有返回值的登录方法 + */ + public Object logout(@NotNull HttpSession session) { + Object userId = APIJSONVerifier.getVisitorId(session);//必须在session.invalidate();前! + Log.d(TAG, "logout userId = " + userId + "; session.getId() = " + (session == null ? null : session.getId())); + session.invalidate(); + return null; + } + + + +// public JSONMap listMethod(String request) { +// if (Log.DEBUG == false) { +// return APIJSONParser.newErrorResult(new IllegalAccessException("非 DEBUG 模式下不允许使用 UnitAuto 单元测试!")); +// } +// return MethodUtil.listMethod(request); +// } +// +// public void invokeMethod(String request, HttpServletRequest servletRequest) { +// AsyncContext asyncContext = servletRequest.startAsync(); +// +// final boolean[] called = new boolean[] { false }; +// MethodUtil.Listener listener = new MethodUtil.Listener() { +// +// @Override +// public void complete(JSONMap data, Method method, InterfaceProxy proxy, Object... extras) throws Exception { +// +// ServletResponse servletResponse = called[0] ? null : asyncContext.getResponse(); +// if (servletResponse == null) { // || servletResponse.isCommitted()) { // isCommitted 在高并发时可能不准,导致写入多次 +// Log.w(TAG, "invokeMethod listener.complete servletResponse == null || servletResponse.isCommitted() >> return;"); +// return; +// } +// called[0] = true; +// +// servletResponse.setCharacterEncoding(servletRequest.getCharacterEncoding()); +// servletResponse.setContentType(servletRequest.getContentType()); +// servletResponse.getWriter().println(data); +// asyncContext.complete(); +// } +// }; +// +// if (Log.DEBUG == false) { +// try { +// listener.complete(MethodUtil.JSON_CALLBACK.newErrorResult(new IllegalAccessException("非 DEBUG 模式下不允许使用 UnitAuto 单元测试!"))); +// } +// catch (Exception e1) { +// e1.printStackTrace(); +// asyncContext.complete(); +// } +// +// return; +// } +// +// +// try { +// MethodUtil.invokeMethod(request, null, listener); +// } +// catch (Exception e) { +// Log.e(TAG, "invokeMethod try { JSONMap req = JSON.parseObject(request); ... } catch (Exception e) { \n" + e.getMessage()); +// try { +// listener.complete(MethodUtil.JSON_CALLBACK.newErrorResult(e)); +// } +// catch (Exception e1) { +// e1.printStackTrace(); +// asyncContext.complete(); +// } +// } +// } + +} diff --git a/src/main/java/apijson/framework/javax/APIJSONCreator.java b/src/main/java/apijson/framework/javax/APIJSONCreator.java new file mode 100644 index 0000000..6e5fcc3 --- /dev/null +++ b/src/main/java/apijson/framework/javax/APIJSONCreator.java @@ -0,0 +1,54 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +import apijson.orm.*; + +import java.util.List; +import java.util.Map; + + +/**APIJSON相关创建器 + * @author Lemon + */ +public class APIJSONCreator, L extends List> + implements ParserCreator, VerifierCreator, SQLCreator { + + @Override + public APIJSONParser createParser() { + return new APIJSONParser<>(); + } + + @Override + public APIJSONFunctionParser createFunctionParser() { + return new APIJSONFunctionParser<>(); + } + + @Override + public APIJSONVerifier createVerifier() { + return new APIJSONVerifier<>(); + } + + @Override + public APIJSONSQLConfig createSQLConfig() { + return new APIJSONSQLConfig<>(); + } + + @Override + public APIJSONSQLExecutor createSQLExecutor() { + return new APIJSONSQLExecutor<>(); + } + +} diff --git a/src/main/java/apijson/framework/javax/APIJSONFunctionParser.java b/src/main/java/apijson/framework/javax/APIJSONFunctionParser.java new file mode 100644 index 0000000..663b0a0 --- /dev/null +++ b/src/main/java/apijson/framework/javax/APIJSONFunctionParser.java @@ -0,0 +1,800 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +import apijson.*; +import apijson.orm.AbstractFunctionParser; +import apijson.orm.script.JavaScriptExecutor; +import apijson.orm.script.ScriptExecutor; +import javax.servlet.http.HttpSession; +//import unitauto.MethodUtil; +//import unitauto.MethodUtil.Argument; + +import java.rmi.ServerException; +import java.util.*; + +import static apijson.JSON.*; +import static apijson.JSONRequest.KEY_COUNT; +import static apijson.RequestMethod.*; +import static apijson.framework.javax.APIJSONConstant.FUNCTION_; +import static apijson.framework.javax.APIJSONConstant.SCRIPT_; + + +/**可远程调用的函数类 + * @author Lemon + */ +public class APIJSONFunctionParser, L extends List> extends AbstractFunctionParser { + public static final String TAG = "APIJSONFunctionParser"; + + @NotNull + public static final String[] ALL_METHODS; + static { + ALL_METHODS = new String[]{ GET.name(), HEAD.name(), GETS.name(), HEADS.name(), POST.name(), PUT.name(), DELETE.name() }; + } + + private HttpSession session; + public APIJSONFunctionParser() { + this(null); + } + public APIJSONFunctionParser(HttpSession session) { + this(null, null, 0, null, session); + } + public APIJSONFunctionParser(RequestMethod method, String tag, int version, M curObj, HttpSession session) { + super(method, tag, version, curObj); + setSession(session); + } + + public HttpSession getSession() { + return session; + } + public APIJSONFunctionParser setSession(HttpSession session) { + this.session = session; + return this; + } + + @Override + public APIJSONFunctionParser setMethod(RequestMethod method) { + super.setMethod(method); + return this; + } + @Override + public APIJSONFunctionParser setTag(String tag) { + super.setTag(tag); + return this; + } + @Override + public APIJSONFunctionParser setVersion(int version) { + super.setVersion(version); + return this; + } + + /**初始化,加载所有远程函数配置,并校验是否已在应用层代码实现 + * @return + * @throws ServerException + */ + public static > M init() throws ServerException { + return init(false); + } + /**初始化,加载所有远程函数配置,并校验是否已在应用层代码实现 + * @param shutdownWhenServerError + * @return + * @throws ServerException + */ + public static > M init(boolean shutdownWhenServerError) throws ServerException { + return init(shutdownWhenServerError, null); + } + /**初始化,加载所有远程函数配置,并校验是否已在应用层代码实现 + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M init(APIJSONCreator creator) throws ServerException { + return init(false, creator); + } + /**初始化,加载所有远程函数配置,并校验是否已在应用层代码实现 + * @param shutdownWhenServerError + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M init(boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { + return init(shutdownWhenServerError, creator, null); + } + /**初始化,加载所有远程函数配置,并校验是否已在应用层代码实现 + * @param shutdownWhenServerError + * @param creator + * @param table 表内自定义数据过滤条件 + * @return + * @throws ServerException + */ + @SuppressWarnings("unchecked") + public static , L extends List> M init(boolean shutdownWhenServerError + , APIJSONCreator creator, M table) throws ServerException { + if (creator == null) { + creator = (APIJSONCreator) APIJSONApplication.DEFAULT_APIJSON_CREATOR; + } + + boolean isAll = table == null || table.isEmpty(); + + //JSONRequest function = isAll ? JSON.createJSONObject() : table; + //if (Log.DEBUG == false) { + // function.put(APIJSONConstant.KEY_DEBUG, 0); + //} + // + //JSONRequest functionItem = JSON.createJSONObject(); + //functionItem.put(FUNCTION_, function); + // + //JSONRequest script = JSON.createJSONObject(); // isAll ? JSON.createJSONObject() : table; + //script.put("simple", 0); + //if (Log.DEBUG == false) { + // script.put(APIJSONConstant.KEY_DEBUG, 0); + //} + // 不能用这个来优化,因为可能配置了不校验远程函数是否存在 + //{ // name{}@ <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + //JSONRequest nameInAt = JSON.createJSONObject(); + //nameInAt.put("from", "Function"); + //{ // Function <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + // JSONRequest fun = JSON.createJSONObject(); + // fun.setColumn("name"); + // nameInAt.put("Function", fun); + //} // Function >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + //script.put("name{}@", nameInAt); + //} // name{}@ >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + //JSONRequest scriptItem = JSON.createJSONObject(); + //scriptItem.put(SCRIPT_, script); + + M request = JSON.createJSONObject(); + //request.putAll(functionItem.toArray(0, 0, FUNCTION_)); + //request.putAll(scriptItem.toArray(0, 0, SCRIPT_)); + + // 可以用它,因为 Function 表必须存在,没有绕过校验的配置 // 不能用这个来优化,因为可能配置了不校验远程函数是否存在 + { // [] <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + M item = JSON.createJSONObject(); + + { // Function <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + M function = isAll ? JSON.createJSONObject() : table; + if (! Log.DEBUG) { + function.put(APIJSONConstant.KEY_DEBUG, 0); + } + item.put(FUNCTION_, function); + } // Function >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + if (ENABLE_SCRIPT_FUNCTION) { // Script <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + M script = JSON.createJSONObject(); + script.put("name@", "/Function/name"); + script.put("simple", 0); + item.put(SCRIPT_, script); + } // Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + item.put(KEY_COUNT, 0); + request.put("[]", item); + } // [] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + + M response = creator.createParser().setMethod(GET).setNeedVerify(true).parseResponse(request); + if (! JSONResponse.isSuccess(response)) { + onServerError("\n\n\n\n\n !!!! 查询远程函数异常 !!!\n" + response.get(JSONResponse.KEY_MSG) + "\n\n\n\n\n", shutdownWhenServerError); + } + + //初始化默认脚本引擎,避免增量 + if (isAll || SCRIPT_EXECUTOR_MAP.get("js") == null) { + ScriptExecutor javaScriptExecutor = new JavaScriptExecutor<>(); + javaScriptExecutor.init(); + SCRIPT_EXECUTOR_MAP.put("js", javaScriptExecutor); + SCRIPT_EXECUTOR_MAP.put("JavaScript", javaScriptExecutor); + SCRIPT_EXECUTOR_MAP.put("javascript", javaScriptExecutor); + } + + Map scriptMap = new HashMap<>(); + L scriptList = JSON.get(response, "[]"); // response.getJSONArray(SCRIPT_ + "[]"); + if (scriptList != null && ! scriptList.isEmpty()) { + //if (isAll) { + // SCRIPT_MAP = new LinkedHashMap<>(); + //} + Map newMap = new LinkedHashMap<>(); + + for (int i = 0; i < scriptList.size(); i++) { + M item = JSON.get(scriptList, i); + item = item == null ? null : JSON.get(item, SCRIPT_); + if (item == null) { // 关联查不到很正常 + continue; + } + + String n = getString(item, "name"); + if (! StringUtil.isName(n)) { + onServerError("Script 表字段 name 的值 " + n + " 不合法!必须为合法的方法名字符串!", shutdownWhenServerError); + } + + String s = getString(item, "script"); + if (StringUtil.isEmpty(s, true)) { + onServerError("Script 表字段 script 的值 " + s + " 不合法!不能为空!", shutdownWhenServerError); + } + newMap.put(n, item); + } + + scriptMap = newMap; + } + + L list = scriptList; // response.getJSONArray(FUNCTION_ + "[]"); + int size = list == null ? 0 : list.size(); + if (isAll && size <= 0) { + Log.w(TAG, "init isAll && size <= 0,,没有可用的远程函数"); + return response; + } + + + if (isAll) { // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! 如果要做成完全校验通过才更新 FUNCTION_MAP,但又不提供 忽略校验 参数,似乎无解 + FUNCTION_MAP = new LinkedHashMap<>(); + } + Map> newMap = FUNCTION_MAP; // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! new LinkedHashMap<>(); + + for (int i = 0; i < size; i++) { + M item = JSON.get(list, i); + item = item == null ? null : JSON.get(item, FUNCTION_); + if (item == null) { + continue; + } + + M demo = JSON.parseObject(getString(item, "demo")); + if (demo == null) { + try { + onServerError("字段 demo 的值必须为合法且非 null 的 JSONObejct 字符串!", shutdownWhenServerError); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + String name = getString(item, "name"); + // demo.put(apijson.JSONRequest.KEY_TAG, getString(item, apijson.JSONRequest.KEY_TAG)); + // demo.put(apijson.JSONRequest.KEY_VERSION, item.getInteger(apijson.JSONRequest.KEY_VERSION)); + //加载脚本 + if (item.get("language") != null) { + String language = getString(item, "language"); + // if (SCRIPT_EXECUTOR_MAP.get(language) == null) { + // onServerError("找不到脚本语言 " + language + " 对应的执行引擎!请先依赖相关库并在后端 APIJSONFunctionParser 中注册!", shutdownWhenServerError); + // } + //脚本语言执行 + if (SCRIPT_EXECUTOR_MAP.containsKey(language)){ + ScriptExecutor scriptExecutor = (ScriptExecutor) SCRIPT_EXECUTOR_MAP.get(language); + M script = scriptMap.get(name); + scriptExecutor.load(name, getString(script, "script")); + } + } + newMap.put(name, item); // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! + + String[] methods = StringUtil.split(getString(item, "methods")); + + if (methods == null || methods.length <= 0) { + methods = ALL_METHODS; + } + + if (demo != null){ + if (! demo.containsKey("result()")) { + demo.put("result()", getFunctionCall(name, getString(item, "arguments"))); + } + demo.put(apijson.JSONRequest.KEY_TAG, item.get(apijson.JSONRequest.KEY_TAG)); + demo.put(apijson.JSONRequest.KEY_VERSION, item.get(apijson.JSONRequest.KEY_VERSION)); + } + + for (String method : methods) { + APIJSONParser parser = APIJSONApplication.createParser(); + M r = parser.setMethod(RequestMethod.valueOf(method)) + .setNeedVerify(false) + .parseResponse(demo); + + if (! JSONResponse.isSuccess(r)) { + try { + onServerError(JSONResponse.getMsg(r), shutdownWhenServerError); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + } + + // 必须在测试 invoke 前把配置 put 进 FUNCTION_MAP! + // if (isAll) { + // FUNCTION_MAP = newMap; + // } + // else { + // FUNCTION_MAP.putAll(newMap); + // } + + return response; + } + + + protected static void onServerError(String msg, boolean shutdown) throws ServerException { + Log.e(TAG, "\n远程函数文档测试未通过!\n请新增 demo 里的函数,或修改 Function 表里的 demo 为已有的函数示例!\n保证前端看到的远程函数文档是正确的!!!\n\n原因:\n" + msg); + + if (shutdown) { + System.exit(1); + } else { + throw new ServerException(msg); + } + } + + + public static void test() throws Exception { + test(null); + } + public static , L extends List> void test( + APIJSONFunctionParser functionParser) throws Exception { + int i0 = 1, i1 = -2; + M request = JSON.createJSONObject(); + request.put("id", 10); + request.put("i0", i0); + request.put("i1", i1); + L arr = JSON.createJSONArray(); + arr.add(JSON.createJSONObject()); + request.put("arr", arr); + + L array = JSON.createJSONArray(); + array.add(1);//JSON.createJSONObject()); + array.add(2);//JSON.createJSONObject()); + array.add(4);//JSON.createJSONObject()); + array.add(10);//JSON.createJSONObject()); + request.put("array", array); + + request.put("position", 1); + request.put("@position", 0); + + request.put("key", "key"); + M object = JSON.createJSONObject(); + object.put("key", "success"); + request.put("object", object); + + APIJSONParser parser = APIJSONApplication.createParser(); + parser.setRequest(request); + if (functionParser == null) { + functionParser = APIJSONApplication.createFunctionParser(); + functionParser.setParser(parser); + functionParser.setMethod(parser.getMethod()); + functionParser.setTag(parser.getTag()); + functionParser.setVersion(parser.getVersion()); + functionParser.setRequest(parser.getRequest()); + + //if (functionParser instanceof APIJSONFunctionParser) { + ((APIJSONFunctionParser) functionParser).setSession(parser.getSession()); + //} + } + + // functionParser.setKey(null); + // functionParser.setParentPath(null); + // functionParser.setCurrentName(null); + functionParser.setCurrentObject(request); + + // 等数据库 Function 表加上 plus 配置再过两个以上迭代(应该是到 5.0)后再取消注释 + // Log.i(TAG, "plus(1,-2) = " + function.invoke("plus(i0,i1)", request)); + // AssertUtil.assertEqual(-1, function.invoke("plus(i0,i1)", request)); + + Log.i(TAG, "count([1,2,4,10]) = " + functionParser.invoke("countArray(array)", request)); + AssertUtil.assertEqual(4, functionParser.invoke("countArray(array)", request)); + + Log.i(TAG, "isContain([1,2,4,10], 10) = " + functionParser.invoke("isContain(array,id)", request)); + AssertUtil.assertEqual(true, functionParser.invoke("isContain(array,id)", request)); + + Log.i(TAG, "getFromArray([1,2,4,10], 0) = " + functionParser.invoke("getFromArray(array,@position)", request)); + AssertUtil.assertEqual(1, functionParser.invoke("getFromArray(array,@position)", request)); + + Log.i(TAG, "getFromObject({key:\"success\"}, key) = " + functionParser.invoke("getFromObject(object,key)", request)); + AssertUtil.assertEqual("success", functionParser.invoke("getFromObject(object,key)", request)); + + } + + + /**获取远程函数的demo,如果没有就自动补全 + * @param curObj + * @return + * @throws ServerException + */ + public M getFunctionDemo(@NotNull M curObj) { + M demo = JSON.parseObject(getString(curObj, "demo")); + if (demo == null) { + demo = JSON.createJSONObject(); + } + if (! demo.containsKey("result()")) { + demo.put("result()", getFunctionCall(getString(curObj, "name"), getString(curObj, "arguments"))); + } + return demo; + } + + /**获取远程函数的demo,如果没有就自动补全 + * @param curObj + * @return + */ + public String getFunctionDetail(@NotNull M curObj) { + return getFunctionCall(getString(curObj, "name"), getString(curObj, "arguments")) + + ": " + StringUtil.trim(getString(curObj, "detail")); + } + /**获取函数调用代码 + * @param name + * @param arguments + * @return + */ + private static String getFunctionCall(String name, String arguments) { + return name + "(" + StringUtil.trim(arguments) + ")"; + } + + + public double plus(@NotNull M curObj, String i0, String i1) { + return getDoubleValue(curObj, i0) + getDoubleValue(curObj, i1); + } + public double minus(@NotNull M curObj, String i0, String i1) { + return getDoubleValue(curObj, i0) - getDoubleValue(curObj, i1); + } + public double multiply(@NotNull M curObj, String i0, String i1) { + return getDoubleValue(curObj, i0) * getDoubleValue(curObj, i1); + } + public double divide(@NotNull M curObj, String i0, String i1) { + return getDoubleValue(curObj, i0) / getDoubleValue(curObj, i1); + } + + public double plus(@NotNull M curObj, Number n0, Number n1) { + return n0.doubleValue() + n1.doubleValue(); + } + public double minus(@NotNull M curObj, Number n0, Number n1) { + return n0.doubleValue() - n1.doubleValue(); + } + public double multiply(@NotNull M curObj, Number n0, Number n1) { + return n0.doubleValue() * n1.doubleValue(); + } + public double divide(@NotNull M curObj, Number n0, Number n1) { + return n0.doubleValue() / n1.doubleValue(); + } + + //判断是否为空 <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + /**判断array是否为空 + * @param curObj + * @param array + * @return + */ + public boolean isArrayEmpty(@NotNull M curObj, String array) { + return BaseModel.isEmpty((Collection) getJSONArray(curObj, array)); + } + /**判断object是否为空 + * @param curObj + * @param object + * @return + */ + public boolean isObjectEmpty(@NotNull M curObj, String object) { + return BaseModel.isEmpty((Map) getJSONObject(curObj, object)); + } + //判断是否为空 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + //判断是否为包含 <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + /**判断array是否包含value + * @param curObj + * @param array + * @param value + * @return + */ + public boolean isContain(@NotNull M curObj, String array, String value) { + //解决isContain((List) [82001,...], (Integer) 82001) == false及类似问题, list元素可能是从数据库查到的bigint类型的值 + // return BaseModel.isContain(getJSONArray(curObj, array), curObj.get(value)); + + //不用准确的的 getString(curObj, value).getClass() ,因为Long值转Integer崩溃,而且转成一种类型本身就和字符串对比效果一样了。 + List list = JSON.parseArray(getString(curObj, array), String.class); + return list != null && list.contains(getString(curObj, value)); + } + /**判断object是否包含key + * @param curObj + * @param object + * @param key + * @return + */ + public boolean isContainKey(@NotNull M curObj, String object, String key) { + return BaseModel.isContainKey(getJSONObject(curObj, object), getString(curObj, key)); + } + /**判断object是否包含value + * @param curObj + * @param object + * @param value + * @return + */ + public boolean isContainValue(@NotNull M curObj, String object, String value) { + return BaseModel.isContainValue(getJSONObject(curObj, object), curObj.get(value)); + } + //判断是否为包含 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + + //获取集合长度 <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + /**获取数量 + * @param curObj + * @param array + * @return + */ + public int countArray(@NotNull M curObj, String array) { + return BaseModel.count((Collection) getJSONArray(curObj, array)); + } + /**获取数量 + * @param curObj + * @param object + * @return + */ + public int countObject(@NotNull M curObj, String object) { + return BaseModel.count((Map) getJSONObject(curObj, object)); + } + //获取集合长度 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + + //根据键获取值 <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + /**获取 + ** @param curObj + * @param array + * @param position 支持直接传数字,例如 getFromArray(array,0) ;或者引用当前对象的值,例如 "@position": 0, "result()": "getFromArray(array,@position)" + * @return + */ + public Object getFromArray(@NotNull M curObj, String array, String position) { + int p; + try { + p = Integer.parseInt(position); + } catch (Exception e) { + p = getIntValue(curObj, position); + } + return BaseModel.get(getJSONArray(curObj, array), p); + } + /**获取 + * @param curObj + * @param object + * @param key + * @return + */ + public Object getFromObject(@NotNull M curObj, String object, String key) { + return BaseModel.get(getJSONObject(curObj, object), getString(curObj, key)); + } + //根据键获取值 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + //根据键移除值 <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + /**移除 + ** @param curObj + * @param position 支持直接传数字,例如 getFromArray(array,0) ;或者引用当前对象的值,例如 "@position": 0, "result()": "getFromArray(array,@position)" + * @return + */ + public Object removeIndex(@NotNull M curObj, String position) { + int p; + try { + p = Integer.parseInt(position); + } catch (Exception e) { + p = getIntValue(curObj, position); + } + curObj.remove(p); + return null; + } + /**移除 + * @param curObj + * @param key + * @return + */ + public Object removeKey(@NotNull M curObj, String key) { + curObj.remove(key); + return null; + } + //根据键获取值 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + + + //获取非基本类型对应基本类型的非空值 <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + /**获取非空值 + * @param curObj + * @param value + * @return + */ + public boolean booleanValue(@NotNull M curObj, String value) { + return getBooleanValue(curObj, value); + } + /**获取非空值 + * @param curObj + * @param value + * @return + */ + public int intValue(@NotNull M curObj, String value) { + return getIntValue(curObj, value); + } + /**获取非空值 + * @param curObj + * @param value + * @return + */ + public long longValue(@NotNull M curObj, String value) { + return getLongValue(curObj, value); + } + /**获取非空值 + * @param curObj + * @param value + * @return + */ + public float floatValue(@NotNull M curObj, String value) { + return getFloatValue(curObj, value); + } + /**获取非空值 + * @param curObj + * @param value + * @return + */ + public double doubleValue(@NotNull M curObj, String value) { + return getDoubleValue(curObj, value); + } + //获取非基本类型对应基本类型的非空值 >>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /**获取value,当value为null时获取defaultValue + * @param curObj + * @param value + * @param defaultValue + * @return v == null ? curObj.get(defaultValue) : v + */ + public Object getWithDefault(@NotNull M curObj, String value, String defaultValue) { + Object v = curObj.get(value); + return v == null ? curObj.get(defaultValue) : v; + } + + // FIXME UnitAuto 去除 fastjson 后恢复 + ///**获取方法参数的定义 + // * @param curObj + // * @return + // * @throws IOException + // * @throws ClassNotFoundException + // * @throws IllegalArgumentException + // */ + //public String getMethodArguments(@NotNull M curObj) throws IllegalArgumentException, ClassNotFoundException, IOException { + // return getMethodArguments(curObj, "methodArgs"); + //} + ///**获取方法参数的定义 + // * @param curObj + // * @param methodArgsKey + // * @return + // * @throws IllegalArgumentException + // * @throws ClassNotFoundException + // * @throws IOException + // */ + //public String getMethodArguments(@NotNull M curObj, String methodArgsKey) throws IllegalArgumentException, ClassNotFoundException, IOException { + // M obj = getJSONObject(curObj, "request"); + // String argsStr = obj == null ? null : getString(obj, methodArgsKey); + // if (StringUtil.isEmpty(argsStr, true)) { + // argsStr = getString(curObj, methodArgsKey); + // } + // List methodArgs = JSON.parseArray(removeComment(argsStr), Argument.class); + // if (methodArgs == null || methodArgs.isEmpty()) { + // return ""; + // } + // + // // Class[] types = new Class[methodArgs.size()]; + // // Object[] args = new Object[methodArgs.size()]; + // // MethodUtil.initTypesAndValues(methodArgs, types, args, true); + // + // String s = ""; + // // if (types != null) { + // // String sn; + // // for (int i = 0; i < types.length; i++) { + // // sn = types[i] == null ? null : types[i].getSimpleName(); + // // if (sn == null) { + // // sn = Object.class.getSimpleName(); + // // } + // // + // // if (i > 0) { + // // s += ","; + // // } + // // + // // if (MethodUtil.CLASS_MAP.containsKey(sn)) { + // // s += sn; + // // } + // // else { + // // s += types[i].getName(); + // // } + // // } + // // } + // + // for (int i = 0; i < methodArgs.size(); i++) { + // Argument arg = methodArgs.get(i); + // + // String sn = arg == null ? null : arg.getType(); + // if (sn == null) { + // sn = arg.getValue() == null ? Object.class.getSimpleName() : MethodUtil.trimType(arg.getValue().getClass()); + // } + // + // if (i > 0) { + // s += ","; + // } + // s += sn; + // } + // + // return s; + //} + // + // + ///**获取方法的定义 + // * @param curObj + // * @return + // * @throws IOException + // * @throws ClassNotFoundException + // * @throws IllegalArgumentException + // */ + //public String getMethodDefinition(@NotNull M curObj) throws IllegalArgumentException { + // // curObj.put("arguments", removeComment(getString(curObj, "methodArgs"))); + // return getMethodDefinition(curObj, "method", "arguments", "genericType", "genericExceptions", "Java"); + //} + ///**获取方法的定义 + // * @param curObj + // * @param method + // * @param arguments + // * @param type + // * @return method(argType0,argType1...): returnType + // * @throws IOException + // * @throws ClassNotFoundException + // * @throws IllegalArgumentException + // */ + //public String getMethodDefinition(@NotNull M curObj, String method, String arguments + // , String type, String exceptions, String language) throws IllegalArgumentException { + // String n = getString(curObj, method); + // if (StringUtil.isEmpty(n, true)) { + // throw new NullPointerException("getMethodDefination StringUtil.isEmpty(methodArgs, true) !"); + // } + // String a = getString(curObj, arguments); + // String t = getString(curObj, type); + // String e = getString(curObj, exceptions); + // + // if (language == null) { + // language = ""; + // } + // switch (language) { + // case "TypeScript": + // return n + "(" + (StringUtil.isEmpty(a, true) ? "" : a) + ")" + (StringUtil.isEmpty(t, true) ? "" : ": " + t) + (StringUtil.isEmpty(e, true) ? "" : " throws " + e); + // case "Go": + // return n + "(" + (StringUtil.isEmpty(a, true) ? "" : a ) + ")" + (StringUtil.isEmpty(t, true) ? "" : " " + t) + (StringUtil.isEmpty(e, true) ? "" : " throws " + e); + // default: + // //类型可能很长,Eclipse, Idea 代码提示都是类型放后面 return (StringUtil.isEmpty(t, true) ? "" : t + " ") + n + "(" + (StringUtil.isEmpty(a, true) ? "" : a) + ")"; + // return n + "(" + (StringUtil.isEmpty(a, true) ? "" : a) + ")" + (StringUtil.isEmpty(t, true) ? "" : ": " + t) + (StringUtil.isEmpty(e, true) ? "" : " throws " + e); + // } + //} + // + ///** + // * methodArgs 和 classArgs 都可以带注释 + // */ + //public String getMethodRequest(@NotNull M curObj) { + // String req = getString(curObj, "request"); + // if (StringUtil.isEmpty(req, true) == false) { + // return req; + // } + // + // req = "{"; + // Boolean isStatic = getBoolean(curObj, "static"); + // String methodArgs = getString(curObj, "methodArgs"); + // String classArgs = getString(curObj, "classArgs"); + // + // boolean comma = false; + // if (isStatic != null && isStatic) { + // req += "\n \"static\": " + true; + // comma = true; + // } + // if (! StringUtil.isEmpty(methodArgs, true)) { + // req += (comma ? "," : "") + "\n \"methodArgs\": " + methodArgs; + // comma = true; + // } + // if (! StringUtil.isEmpty(classArgs, true)) { + // req += (comma ? "," : "") + "\n \"classArgs\": " + classArgs; + // } + // req += "\n}"; + // return req; + //} + // + //// public static JSONRequest removeComment(String json) { + //// return JSON.parseObject(removeComment(json)); + //// } + //public static String removeComment(String json) { + // return json == null ? null: json.replaceAll("(//.*)|(/\\*[\\s\\S]*?\\*/)", ""); + //} + +} diff --git a/src/main/java/apijson/framework/javax/APIJSONObjectParser.java b/src/main/java/apijson/framework/javax/APIJSONObjectParser.java new file mode 100755 index 0000000..c7f79ce --- /dev/null +++ b/src/main/java/apijson/framework/javax/APIJSONObjectParser.java @@ -0,0 +1,71 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +import apijson.NotNull; +import apijson.RequestMethod; +import apijson.framework.javax.*; +import apijson.orm.AbstractObjectParser; +import apijson.orm.Join; +import apijson.orm.Parser; +import apijson.orm.SQLConfig; +import javax.servlet.http.HttpSession; + +import java.util.List; +import java.util.Map; + + +/**简化Parser,getObject和getArray(getArrayConfig)都能用 + * @author Lemon + */ +public class APIJSONObjectParser, L extends List> + extends AbstractObjectParser { + public static final String TAG = "APIJSONObjectParser"; + + /**for single object + * @param session + * @param request + * @param parentPath + * @param arrayConfig + * @param isSubquery + * @param isTable + * @param isArrayMainTable + * @throws Exception + */ + public APIJSONObjectParser(HttpSession session, @NotNull M request, String parentPath, SQLConfig arrayConfig + , boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception { + super(request, parentPath, arrayConfig, isSubquery, isTable, isArrayMainTable); + } + + @Override + public APIJSONObjectParser setMethod(RequestMethod method) { + super.setMethod(method); + return this; + } + + @Override + public APIJSONObjectParser setParser(Parser parser) { + super.setParser(parser); + return this; + } + + + @Override + public SQLConfig newSQLConfig(RequestMethod method, String table, String alias, M request + , List> joinList, boolean isProcedure) throws Exception { + return APIJSONSQLConfig.newSQLConfig(method, table, alias, request, joinList, isProcedure); + } + +} diff --git a/src/main/java/apijson/framework/javax/APIJSONParser.java b/src/main/java/apijson/framework/javax/APIJSONParser.java new file mode 100755 index 0000000..784e307 --- /dev/null +++ b/src/main/java/apijson/framework/javax/APIJSONParser.java @@ -0,0 +1,190 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +import static apijson.framework.javax.APIJSONConstant.DEFAULTS; +import static apijson.framework.javax.APIJSONConstant.FORMAT; +import static apijson.framework.javax.APIJSONConstant.VERSION; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpSession; + +import apijson.NotNull; +import apijson.RequestMethod; +import apijson.orm.AbstractParser; +import apijson.orm.FunctionParser; +import apijson.orm.SQLConfig; + + +/**请求解析器 + * @author Lemon + */ +public class APIJSONParser, L extends List> extends AbstractParser { + public static final String TAG = "APIJSONParser"; + + + public APIJSONParser() { + super(); + } + public APIJSONParser(RequestMethod method) { + super(method); + } + public APIJSONParser(RequestMethod method, boolean needVerify) { + super(method, needVerify); + } + + private HttpSession session; + public HttpSession getSession() { + return session; + } + public APIJSONParser setSession(HttpSession session) { + this.session = session; + setVisitor(APIJSONVerifier.getVisitor(session)); + return this; + } + + @SuppressWarnings("unchecked") + @Override + public APIJSONParser createParser() { + return APIJSONApplication.createParser(); + } + @Override + public APIJSONFunctionParser createFunctionParser() { + return APIJSONApplication.createFunctionParser(); + } + + @SuppressWarnings("unchecked") + @Override + public APIJSONVerifier createVerifier() { + return APIJSONApplication.createVerifier(); + } + + @Override + public APIJSONSQLConfig createSQLConfig() { + return APIJSONApplication.createSQLConfig(); + } + @Override + public APIJSONSQLExecutor createSQLExecutor() { + return APIJSONApplication.createSQLExecutor(); + } + + @Override + public APIJSONParser setNeedVerify(boolean needVerify) { + super.setNeedVerify(needVerify); + return this; + } + + @Override + public APIJSONParser setNeedVerifyLogin(boolean needVerifyLogin) { + super.setNeedVerifyLogin(needVerifyLogin); + return this; + } + + @Override + public APIJSONParser setNeedVerifyRole(boolean needVerifyRole) { + super.setNeedVerifyRole(needVerifyRole); + return this; + } + + @Override + public APIJSONParser setNeedVerifyContent(boolean needVerifyContent) { + super.setNeedVerifyContent(needVerifyContent); + return this; + } + + @Override + public M parseResponse(M request) { + //补充format + if (session != null && request != null) { + if (request.get(FORMAT) == null) { + request.put(FORMAT, session.getAttribute(FORMAT)); + } + if (request.get(VERSION) == null) { + request.put(VERSION, session.getAttribute(VERSION)); + } + + if (request.get(DEFAULTS) == null) { + M defaults = (M) session.getAttribute(DEFAULTS); + Set> set = defaults == null ? null : defaults.entrySet(); + + if (set != null) { + for (Map.Entry e : set) { + if (e != null && request.get(e.getKey()) == null) { + request.put(e.getKey(), e.getValue()); + } + } + } + } + } + + return super.parseResponse(request); + } + + private FunctionParser functionParser; + public FunctionParser getFunctionParser() { + return functionParser; + } + @Override + public Object onFunctionParse(String key, String function, String parentPath, String currentName, M currentObject, boolean containRaw) throws Exception { + if (functionParser == null) { + functionParser = createFunctionParser(); + functionParser.setParser(this); + functionParser.setMethod(getMethod()); + functionParser.setTag(getTag()); + functionParser.setVersion(getVersion()); + functionParser.setRequest(requestObject); + + if (functionParser instanceof APIJSONFunctionParser) { + ((APIJSONFunctionParser) functionParser).setSession(getSession()); + } + } + functionParser.setKey(key); + functionParser.setParentPath(parentPath); + functionParser.setCurrentName(currentName); + functionParser.setCurrentObject(currentObject); + + return functionParser.invoke(function, currentObject, containRaw); + } + + + @Override + public APIJSONObjectParser createObjectParser(@NotNull M request, String parentPath, SQLConfig arrayConfig + , boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception { + + return new APIJSONObjectParser(getSession(), request, parentPath, arrayConfig, isSubquery, isTable, isArrayMainTable) { + + // @Override + // protected APIJSONSQLConfig newQueryConfig() { + // if (itemConfig != null) { + // return itemConfig; + // } + // return super.newQueryConfig(); + // } + + // 导致最多评论的(Strong 30个)的那个动态详情界面Android(82001)无姓名和头像,即User=null + // @Override + // protected void onComplete() { + // if (response != null) { + // putQueryResult(path, response); // 解决获取关联数据时requestObject里不存在需要的关联数据 + // } + // } + + }.setMethod(getMethod()).setParser(this); + } + +} diff --git a/src/main/java/apijson/framework/javax/APIJSONSQLConfig.java b/src/main/java/apijson/framework/javax/APIJSONSQLConfig.java new file mode 100755 index 0000000..e4f757d --- /dev/null +++ b/src/main/java/apijson/framework/javax/APIJSONSQLConfig.java @@ -0,0 +1,422 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +import apijson.RequestMethod; +import apijson.orm.AbstractSQLConfig; +import apijson.orm.Join; +import apijson.orm.SQLConfig; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static apijson.framework.javax.APIJSONConstant.*; + + +/**SQL配置 + * TiDB 用法和 MySQL 一致 + * @author Lemon + */ +public class APIJSONSQLConfig, L extends List> extends AbstractSQLConfig { + public static final String TAG = "APIJSONSQLConfig"; + + public static boolean ENABLE_COLUMN_CONFIG = false; + + public static Callback, ? extends List> SIMPLE_CALLBACK; + + static { + DEFAULT_DATABASE = DATABASE_MYSQL; //TODO 默认数据库类型,改成你自己的 + DEFAULT_SCHEMA = "sys"; //TODO 默认模式名,改成你自己的,默认情况是 MySQL: sys, PostgreSQL: public, SQL Server: dbo, Oracle: + // TABLE_KEY_MAP.put(Access.class.getSimpleName(), "apijson_access"); + + // 由 APIJSONVerifier.init 方法读取数据库 Access 表来替代手动输入配置 + // //表名映射,隐藏真实表名,对安全要求很高的表可以这么做 + // TABLE_KEY_MAP.put(User.class.getSimpleName(), "apijson_user"); + // TABLE_KEY_MAP.put(Privacy.class.getSimpleName(), "apijson_privacy"); + + SIMPLE_CALLBACK = new SimpleCallback, List>() { + + @Override + public SQLConfig, List> getSQLConfig( + RequestMethod method, String database, String schema, String datasource, String table) { + SQLConfig, List> config = APIJSONApplication.createSQLConfig(); + config.setMethod(method); + config.setDatabase(database); + config.setDatasource(datasource); + config.setSchema(schema); + config.setTable(table); + return config; + } + + //取消注释来实现自定义各个表的主键名 + // @Override + // public String getIdKey(String database, String schema, String datasource, String table) { + // return StringUtil.firstCase(table + "Id"); // userId, comemntId ... + // // return StringUtil.toLowerCase(t) + "_id"; // user_id, comemnt_id ... + // // return StringUtil.toUpperCase(t) + "_ID"; // USER_ID, COMMENT_ID ... + // } + + @Override + public String getUserIdKey(String database, String schema, String datasource, String table) { + return USER_.equals(table) || PRIVACY_.equals(table) ? ID : USER_ID; // id / userId + } + + //取消注释来实现数据库自增 id + // @Override + // public Object newId(RequestMethod method, String database, String schema, String datasource, String table) { + // return null; // return null 则不生成 id,一般用于数据库自增 id + // } + }; + + } + + /**获取SQL配置 + * @param table + * @param alias + * @param request + * @param isProcedure + * @return + * @throws Exception + */ + public static , L extends List> SQLConfig newSQLConfig( + RequestMethod method, String table, String alias, M request, List> joinList, boolean isProcedure) throws Exception { + return newSQLConfig(method, table, alias, request, joinList, isProcedure, (SimpleCallback) SIMPLE_CALLBACK); + } + + public APIJSONSQLConfig() { + this(RequestMethod.GET); + } + public APIJSONSQLConfig(RequestMethod method) { + super(method); + } + public APIJSONSQLConfig(RequestMethod method, String table) { + super(method, table); + } + public APIJSONSQLConfig(RequestMethod method, int count, int page) { + super(method, count, page); + } + + + public String gainDBVersion() { + if (isMySQL()) { + return "5.7.22"; //"8.0.11"; //TODO 改成你自己的 MySQL 或 PostgreSQL 数据库版本号 //MYSQL 8 和 7 使用的 JDBC 配置不一样 + } + if (isPostgreSQL()) { + return "9.6.15"; //TODO 改成你自己的 + } + if (isSQLServer()) { + return "2016"; //TODO 改成你自己的 + } + if (isOracle()) { + return "18c"; //TODO 改成你自己的 + } + return null; + } + + public String gainDBUri() { + if (isMySQL()) { + return "jdbc:mysql://localhost:3306"; + } + if (isTiDB()) { + return "jdbc:mysql://localhost:4000"; + } + if (isPostgreSQL()) { // PG JDBC 必须在 URI 传 catalog + return "jdbc:postgresql://localhost:5432/postgres?stringtype=unspecified"; //TODO 改成你自己的 + } + //if (isCockroachDB()) { // PG JDBC 必须在 URI 传 catalog + // return "jdbc:postgresql://localhost:26257/movr?sslmode=require"; //TODO 改成你自己的 brew install cockroachdb/tap/cockroach && cockroach demo + // // return "jdbc:postgresql://localhost:26258/postgres?sslmode=disable"; //TODO 改成你自己的 brew install cockroachdb/tap/cockroach # && start 3 nodes and init cluster + //} + if (isSQLServer()) { + return "jdbc:jtds:sqlserver://localhost:1433/pubs;instance=SQLEXPRESS"; //TODO 改成你自己的 + } + if (isOracle()) { + return "jdbc:oracle:thin:@localhost:1521:orcl"; //TODO 改成你自己的 + } + if (isDb2()) { + return "jdbc:db2://localhost:50000/BLUDB"; //TODO 改成你自己的 + } + if (isSQLite()) { + return "jdbc:sqlite:sample.db"; //TODO 改成你自己的 + } + if (isDameng()) { + return "jdbc:dm://localhost:5236"; //TODO 改成你自己的 + } + if (isTDengine()) { + // return "jdbc:TAOS://localhost:6030"; //TODO 改成你自己的 + return "jdbc:TAOS-RS://localhost:6041"; //TODO 改成你自己的 + } + if (isTimescaleDB()) { // PG JDBC 必须在 URI 传 catalog + return "jdbc:postgresql://localhost:5432/postgres?stringtype=unspecified"; //TODO 改成你自己的 + } + if (isQuestDB()) { // PG JDBC 必须在 URI 传 catalog + return "jdbc:postgresql://localhost:8812/qdb"; //TODO 改成你自己的 + } + if (isInfluxDB()) { + return "http://203.189.6.3:8086"; //TODO 改成你自己的 + } + if (isMilvus()) { + return "http://localhost:19530"; //TODO 改成你自己的 + } + if (isManticore()) { + return "jdbc:mysql://localhost:9306?characterEncoding=utf8&maxAllowedPacket=512000"; + } + if (isIoTDB()) { + return "jdbc:iotdb://localhost:6667"; // ?charset=GB18030 加参数会报错 URI 格式错误 + } + if (isMongoDB()) { + return "jdbc:mongodb://atlas-sql-6593c65c296c5865121e6ebe-xxskv.a.query.mongodb.net/myVirtualDatabase?ssl=true&authSource=admin"; + } + if (isCassandra()) { + return "http://localhost:7001"; + } + if (isDuckDB()) { + return "jdbc:duckdb:/Users/root/my_database.duckdb"; + } + if (isSurrealDB()) { + // return "memory"; + // return "surrealkv://localhost:8000"; + return "ws://localhost:8000"; + } + if (isOpenGauss()) { + return "jdbc:opengauss://127.0.0.1:5432/postgres?currentSchema=" + DEFAULT_SCHEMA; + } + if (isDoris()) { + return "jdbc:mysql://localhost:9030"; + } + return null; + } + + public String gainDBAccount() { + if (isMySQL()) { + return "root"; //TODO 改成你自己的 + } + if (isPostgreSQL()) { + return "postgres"; //TODO 改成你自己的 + } + if (isSQLServer()) { + return "sa"; //TODO 改成你自己的 + } + if (isOracle()) { + return "scott"; //TODO 改成你自己的 + } + if (isMySQL()) { + return "root"; // ""apijson"; //TODO 改成你自己的 + } + if (isPostgreSQL()) { + return "postgres"; //TODO 改成你自己的 + } + //if (isCockroachDB()) { // PG JDBC 必须在 URI 传 catalog + // return "demo"; //TODO 改成你自己的 + // //return "postgres"; //TODO 改成你自己的 + //} + if (isSQLServer()) { + return "sa"; //TODO 改成你自己的 + } + if (isOracle()) { + return "scott"; //TODO 改成你自己的 + } + if (isDb2()) { + return "db2admin"; //TODO 改成你自己的 + } + // if (isSQLite()) { + // return "root"; //TODO 改成你自己的 + // } + if (isDameng()) { + return "SYSDBA"; + } + if (isTDengine()) { + return "root"; //TODO 改成你自己的 + } + //if (isTimescaleDB()) { + // return "postgres"; //TODO 改成你自己的 + //} + if (isQuestDB()) { + return "admin"; //TODO 改成你自己的 + } + if (isInfluxDB()) { + return "iotos"; + } + if (isMilvus()) { + return "root"; + } + if (isManticore()) { + return null; // "root"; + } + if (isIoTDB()) { + return "root"; + } + if (isMongoDB()) { + return "root"; //TODO 改成你自己的 + } + if (isCassandra()) { + return "root"; //TODO 改成你自己的 + } + if (isDuckDB()) { + return "root"; //TODO 改成你自己的 + } + if (isSurrealDB()) { + return "root"; //TODO 改成你自己的 + } + if (isOpenGauss()) { + return "postgres"; //TODO 改成你自己的 + // 不允许用初始账号,需要 CREATE USER 创建新账号并 GRANT 授权 return "opengauss"; //TODO 改成你自己的 + } + if (isDoris()) { + return "root"; //TODO 改成你自己的 + } + + return null; + } + + public String gainDBPassword() { + if (isMySQL()) { + return "yourPassword@123"; + } + if (isTiDB()) { + return ""; + } + if (isPostgreSQL()) { + return null; + } + if (isSQLServer()) { + return "yourPassword@123"; + } + if (isOracle()) { + return "tiger"; + } + //if (isCockroachDB()) { // PG JDBC 必须在 URI 传 catalog + // return "demo39865"; + // // return null + //} + if (isSQLServer()) { + return "yourPassword@123"; + } + if (isOracle()) { + return "tiger"; + } + if (isDb2()) { + return "123"; + } + if (isSQLite()) { + return "yourPassword@123"; + } + if (isDameng()) { + return "SYSDBA"; + } + if (isTDengine()) { + return "taosdata"; + } + if (isTimescaleDB()) { + return "password"; + } + if (isQuestDB()) { + return "quest"; + } + if (isInfluxDB()) { + return "yourPassword@123"; + } + if (isMilvus()) { + return "yourPassword@123"; + } + //if (isManticore()) { + // return null; + //} + //if (isIoTDB()) { + // return "root"; + //} + if (isMongoDB()) { + return "yourPassword@123"; + } + if (isCassandra()) { + return "yourPassword@123"; + } + if (isDuckDB()) { + return ""; + } + if (isSurrealDB()) { + return "root"; + } + if (isOpenGauss()) { + return "yourPassword@123"; + } + if (isDoris()) { + return ""; + } + + return null; + } + + /**获取 APIJSON 配置表所在数据库模式 database,默认与业务表一块 + * @return + */ + public String getConfigDatabase() { + return getDatabase(); + } + /**获取 APIJSON 配置表所在数据库模式 schema,默认与业务表一块 + * @return + */ + public String getConfigSchema() { + return getSchema(); + } + /**是否为 APIJSON 配置表,如果和业务表一块,可以重写这个方法,固定 return false 来提高性能 + * @return + */ + public boolean isConfigTable() { + return CONFIG_TABLE_LIST.contains(getTable()); + } + @Override + public String gainSQLDatabase() { + String db = isConfigTable() ? getConfigDatabase() : super.gainSQLDatabase(); + return db == null ? DEFAULT_DATABASE : db; + } + @Override + public String gainSQLSchema() { + String sch = isConfigTable() ? getConfigSchema() : super.gainSQLSchema(); + return sch == null ? DEFAULT_SCHEMA : sch; + } + + + @Override + public String getIdKey() { + return SIMPLE_CALLBACK.getIdKey(getDatabase(), getSchema(), getDatasource(), getTable()); + } + + @Override + public String getUserIdKey() { + return SIMPLE_CALLBACK.getUserIdKey(getDatabase(), getSchema(), getDatasource(), getTable()); + } + + + // 支持 !key 反选字段 和 字段名映射,依赖插件 https://github.com/APIJSON/apijson-column + @Override + public APIJSONSQLConfig setColumn(List column) { + if (ENABLE_COLUMN_CONFIG) { + column = ColumnUtil.compatInputColumn(column, getTable(), getMethod(), getVersion(), ! isConfigTable()); + } + super.setColumn(column); + return this; + } + + @Override + public String gainKey(String key) { + if (ENABLE_COLUMN_CONFIG) { + key = ColumnUtil.compatInputKey(key, getTable(), getMethod()); + } + return super.gainKey(key); + } + +} diff --git a/src/main/java/apijson/framework/javax/APIJSONSQLExecutor.java b/src/main/java/apijson/framework/javax/APIJSONSQLExecutor.java new file mode 100755 index 0000000..8d87537 --- /dev/null +++ b/src/main/java/apijson/framework/javax/APIJSONSQLExecutor.java @@ -0,0 +1,24 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +import java.util.List; +import java.util.Map; + + +/**executor for query(read) or update(write) MySQL database + * @author Lemon + */ +public class APIJSONSQLExecutor, L extends List> extends apijson.framework.APIJSONSQLExecutor {} \ No newline at end of file diff --git a/src/main/java/apijson/framework/javax/APIJSONVerifier.java b/src/main/java/apijson/framework/javax/APIJSONVerifier.java new file mode 100755 index 0000000..db47db2 --- /dev/null +++ b/src/main/java/apijson/framework/javax/APIJSONVerifier.java @@ -0,0 +1,871 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +import static apijson.JSON.*; +import static apijson.JSONMap.KEY_ORDER; +import static apijson.JSONMap.isTableKey; +import static apijson.JSONRequest.KEY_COUNT; +import static apijson.framework.javax.APIJSONConstant.*; +import static apijson.framework.javax.APIJSONConstant.METHODS; + +import java.rmi.ServerException; +import java.util.*; + +import apijson.*; +import apijson.orm.JSONRequest; +import javax.servlet.http.HttpSession; + +import apijson.orm.*; + + +/**权限验证器 + * @author Lemon + */ +public class APIJSONVerifier, L extends List> extends AbstractVerifier { + public static final String TAG = "APIJSONVerifier"; + + public static boolean ENABLE_VERIFY_COLUMN = true; + public static boolean ENABLE_APIJSON_ROUTER = false; + + // 由 init 方法读取数据库 Access 表来替代手动输入配置 + // // > + // // > + // static { //注册权限 + // ACCESS_MAP.put(User.class.getSimpleName(), getAccessMap(User.class.getAnnotation(MethodAccess.class))); + // ACCESS_MAP.put(Privacy.class.getSimpleName(), getAccessMap(Privacy.class.getAnnotation(MethodAccess.class))); + // ACCESS_MAP.put(Moment.class.getSimpleName(), getAccessMap(Moment.class.getAnnotation(MethodAccess.class))); + // ACCESS_MAP.put(Comment.class.getSimpleName(), getAccessMap(Comment.class.getAnnotation(MethodAccess.class))); + // ACCESS_MAP.put(Verify.class.getSimpleName(), getAccessMap(Verify.class.getAnnotation(MethodAccess.class))); + // ACCESS_MAP.put(Login.class.getSimpleName(), getAccessMap(Login.class.getAnnotation(MethodAccess.class))); + // } + + public static Map>> DOCUMENT_MAP; + + static { + DOCUMENT_MAP = new HashMap<>(); + } + + /**初始化,加载所有权限配置和请求校验配置 + * @return + * @throws ServerException + */ + public static , L extends List> M init() throws ServerException { + return init(false); + } + + /**初始化,加载所有权限配置和请求校验配置 + * @param shutdownWhenServerError + * @return + * @throws ServerException + */ + public static , L extends List> M init(boolean shutdownWhenServerError) throws ServerException { + return init(shutdownWhenServerError, new APIJSONCreator() { + + }); + } + + /**初始化,加载所有权限配置和请求校验配置 + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M init(APIJSONCreator creator) throws ServerException { + return init(false, creator); + } + + /**初始化,加载所有权限配置和请求校验配置 + * @param shutdownWhenServerError + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M init( + boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { + M result = JSON.createJSONObject(); + if (ENABLE_VERIFY_ROLE) { + result.put(ACCESS_, initAccess(shutdownWhenServerError, creator)); + } + if (ENABLE_VERIFY_CONTENT) { + result.put(REQUEST_, initRequest(shutdownWhenServerError, creator)); + } + return result; + } + + /**初始化,加载所有权限配置 + * @return + * @throws ServerException + */ + public static , L extends List> M initAccess() throws ServerException { + return initAccess(false); + } + + /**初始化,加载所有权限配置 + * @param shutdownWhenServerError + * @return + * @throws ServerException + */ + public static , L extends List> M initAccess(boolean shutdownWhenServerError) throws ServerException { + return initAccess(shutdownWhenServerError, null); + } + + /**初始化,加载所有权限配置 + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M initAccess(APIJSONCreator creator) throws ServerException { + return initAccess(false, creator); + } + + /**初始化,加载所有权限配置 + * @param shutdownWhenServerError + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M initAccess( + boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { + return initAccess(shutdownWhenServerError, creator, null); + } + + /**初始化,加载所有权限配置 + * @param shutdownWhenServerError + * @param creator + * @param table 表内自定义数据过滤条件 + * @return + * @throws ServerException + */ + @SuppressWarnings("unchecked") + public static , L extends List> M initAccess( + boolean shutdownWhenServerError, APIJSONCreator creator, M table) throws ServerException { + if (creator == null) { + creator = (APIJSONCreator) APIJSONApplication.DEFAULT_APIJSON_CREATOR; + } + + boolean isAll = table == null || table.isEmpty(); + + M access = isAll ? JSON.createJSONObject() : table; + if (Log.DEBUG == false) { + access.put(APIJSONConstant.KEY_DEBUG, 0); + } + M accessItem = JSON.createJSONObject(); + accessItem.put(ACCESS_, access); + accessItem.put(KEY_COUNT, 0); + + M request = JSON.createJSONObject(); + request.put(ACCESS_ + "[]", accessItem); + + M response = creator.createParser().setMethod(RequestMethod.GET).setNeedVerify(false).parseResponse(request); + if (JSONResponse.isSuccess(response) == false) { + Log.e(TAG, "\n\n\n\n\n !!!! 查询权限配置异常 !!!\n" + getString(response, JSONResponse.KEY_MSG) + "\n\n\n\n\n"); + onServerError("查询权限配置异常 !", shutdownWhenServerError); + } + + L list = getJSONArray(response, ACCESS_ + "[]"); + int size = list == null ? 0 : list.size(); + if (isAll && size <= 0) { + Log.w(TAG, "initAccess isAll && size <= 0,,没有可用的权限配置"); + return response; + } + + Log.d(TAG, "initAccess < for ACCESS_MAP.size() = " + ACCESS_MAP.size() + " <<<<<<<<<<<<<<<<<<<<<<<<"); + + Map> newMap = new LinkedHashMap<>(); + Map> fakeDeleteMap = new LinkedHashMap<>(); + Map newTKMap = new LinkedHashMap<>(); // JSON.createJSONObject(); + Map tableSchemaMap = new LinkedHashMap<>(); // JSON.createJSONObject(); + + SortedMap>> versionedTableColumnMap = new TreeMap<>(ColumnUtil.DESC_COMPARATOR); + SortedMap>> versionedKeyColumnMap = new TreeMap<>(ColumnUtil.DESC_COMPARATOR); + for (int i = 0; i < size; i++) { + M item = getJSONObject(list, i); + if (item == null) { + continue; + } + + Map map = new HashMap<>(); + // fastjson2 不支持 + //map.put(RequestMethod.GET, JSON.parseObject(getString(item, "get"), String[].class)); + //map.put(RequestMethod.HEAD, JSON.parseObject(getString(item, "head"), String[].class)); + //map.put(RequestMethod.GETS, JSON.parseObject(getString(item, "gets"), String[].class)); + //map.put(RequestMethod.HEADS, JSON.parseObject(getString(item, "heads"), String[].class)); + //map.put(RequestMethod.POST, JSON.parseObject(getString(item, "post"), String[].class)); + //map.put(RequestMethod.PUT, JSON.parseObject(getString(item, "put"), String[].class)); + //map.put(RequestMethod.DELETE, JSON.parseObject(getString(item, "delete"), String[].class)); + + List getArr = parseArray(getString(item, "get"), String.class); + map.put(RequestMethod.GET, getArr == null ? null : getArr.toArray(new String[]{})); + + List headArr = parseArray(getString(item, "head"), String.class); + map.put(RequestMethod.HEAD, headArr == null ? null : headArr.toArray(new String[]{})); + + List getsArr = parseArray(getString(item, "gets"), String.class); + map.put(RequestMethod.GETS, getsArr == null ? null : getsArr.toArray(new String[]{})); + + List headsArr = parseArray(getString(item, "heads"), String.class); + map.put(RequestMethod.HEADS, headsArr == null ? null : headsArr.toArray(new String[]{})); + + List postArr = parseArray(getString(item, "post"), String.class); + map.put(RequestMethod.POST, postArr == null ? null : postArr.toArray(new String[]{})); + + List putArr = parseArray(getString(item, "put"), String.class); + map.put(RequestMethod.PUT, putArr == null ? null : putArr.toArray(new String[]{})); + + List deleteArr = parseArray(getString(item, "delete"), String.class); + map.put(RequestMethod.DELETE, deleteArr == null ? null : deleteArr.toArray(new String[]{})); + + String name = getString(item, "name"); + String alias = getString(item, "alias"); + String schema = getString(item, "schema"); + + Map fakeMap = new LinkedHashMap<>(); + String deletedKey = getString(item, AbstractSQLConfig.KEY_DELETED_KEY); + if(StringUtil.isNotEmpty(deletedKey, true)) { + boolean containNotDeletedValue = item.containsKey(AbstractSQLConfig.KEY_NOT_DELETED_VALUE); + Object deletedValue = getString(item, AbstractSQLConfig.KEY_DELETED_VALUE); + if (containNotDeletedValue == false && StringUtil.isEmpty(deletedValue, true)) { + onServerError( + "Access表 id = " + getString(item, "id") + " 对应的 " + + AbstractSQLConfig.KEY_DELETED_VALUE + " 的值不能为空!或者必须包含字段 " + + AbstractSQLConfig.KEY_NOT_DELETED_VALUE + " !" + , shutdownWhenServerError + ); + } + fakeMap.put(AbstractSQLConfig.KEY_DELETED_KEY, deletedKey); + fakeMap.put(AbstractSQLConfig.KEY_DELETED_VALUE, deletedValue); + if (containNotDeletedValue) { + fakeMap.put(AbstractSQLConfig.KEY_NOT_DELETED_VALUE, item.get(AbstractSQLConfig.KEY_NOT_DELETED_VALUE)); + } + } + + /**TODO + * 以下判断写得比较复杂,因为表设计不够好,但为了兼容旧版 APIJSON 服务 和 APIAuto 工具而保留了下来。 + * 如果是 name 为接口传参的 表对象 的 key,对应一个可缺省的 tableName,判断就会简单不少。 + */ + + if (StringUtil.isEmpty(name, true)) { + onServerError("字段 name 的值不能为空!", shutdownWhenServerError); + } + + if (StringUtil.isEmpty(alias, true)) { + if (isTableKey(name) == false) { + onServerError("name: " + name + "不合法!字段 alias 的值为空时,name 必须为合法表名!", shutdownWhenServerError); + } + + alias = name; + } else if (isTableKey(alias) == false) { + onServerError("alias: " + alias + "不合法!字段 alias 的值只能为 空 或者 合法表名!", shutdownWhenServerError); + } + + newMap.put(alias, map); + fakeDeleteMap.put(alias, fakeMap); + newTKMap.put(alias, name); + tableSchemaMap.put(alias, schema); + + if (ENABLE_VERIFY_COLUMN) { + M columns = getJSONObject(item, "columns"); + Set> set = columns == null ? null : columns.entrySet(); + if (set != null) { + + for (Map.Entry entry : set) { + Integer version = entry == null ? null : Integer.valueOf(entry.getKey()); // null is not possible + Object val = version == null ? null : entry.getValue(); + if (val == null) { + continue; + } + + Map> kcm = new LinkedHashMap<>(); // versionedKeyColumnMap.get(version); + Map cm = new LinkedHashMap<>(); // kcm.get(alias); + + String[] cs = StringUtil.split(String.valueOf(val)); + List l = new ArrayList<>(); + for (int j = 0; j < cs.length; j++) { + String s = cs[j]; + Entry ety = Pair.parseEntry(s, true); + String k = ety == null ? null : ety.getKey(); + String v = ety == null ? null : ety.getValue(); + if (StringUtil.isName(k) == false || (v != null && StringUtil.isName(v) == false)) { + throw new IllegalArgumentException("后端 Access 表中 name: " + name + " 对应 columns 字段的值 " + + version + ":value 中第 " + j + " 个字段 column:alias 中字符 " + s + " 不合法!" + + "alias 可缺省,但 column, alias 都必须为合法的变量名!" + + " ! !ety == null || StringUtil.isName(ety.getKey()) == false " + + " || (ety.getValue() != null && StringUtil.isName(ety.getValue()) == false)"); + } + + l.add(k); + + cm.put(v == null ? k : v, k); + +// if (v != null) { +//// if (kcm == null) { +//// kcm = new LinkedHashMap<>(); +//// } +//// if (m == null) { +//// m = new LinkedHashMap<>(); +//// kcm.put(alias, m); +//// } +// cm.put(v, k); +// } + } + + Map> m = new LinkedHashMap<>(); + m.put(alias, l); + versionedTableColumnMap.put(version, m); + + kcm.put(alias, cm); + versionedKeyColumnMap.put(version, kcm); + } + } + } + } + + if (isAll) { // 全量更新 + ACCESS_MAP = newMap; + ACCESS_FAKE_DELETE_MAP = fakeDeleteMap; + APIJSONSQLConfig.TABLE_KEY_MAP = newTKMap; + APIJSONSQLConfig.TABLE_SCHEMA_MAP = tableSchemaMap; + } else { + ACCESS_MAP.putAll(newMap); + ACCESS_FAKE_DELETE_MAP.putAll(fakeDeleteMap); + APIJSONSQLConfig.TABLE_KEY_MAP.putAll(newTKMap); + APIJSONSQLConfig.TABLE_SCHEMA_MAP = tableSchemaMap; + } + +// if (ENABLE_VERIFY_COLUMN) { +// if (isAll) { // 全量更新 +// ColumnUtil.VERSIONED_TABLE_COLUMN_MAP = versionedTableColumnMap; +// ColumnUtil.VERSIONED_KEY_COLUMN_MAP = versionedKeyColumnMap; +// } else { +// ColumnUtil.VERSIONED_TABLE_COLUMN_MAP.putAll(versionedTableColumnMap); +// ColumnUtil.VERSIONED_KEY_COLUMN_MAP.putAll(versionedKeyColumnMap); +// } +// ColumnUtil.init(); +// } + + Log.d(TAG, "initAccess for /> ACCESS_MAP.size() = " + ACCESS_MAP.size() + " >>>>>>>>>>>>>>>>>>>>>>>"); + + return response; + } + + + /**初始化,加载所有请求校验配置 + * @return + * @throws ServerException + */ + public static , L extends List> M initRequest() throws ServerException { + return initRequest(false); + } + + /**初始化,加载所有请求校验配置 + * @param shutdownWhenServerError + * @return + * @throws ServerException + */ + public static , L extends List> M initRequest(boolean shutdownWhenServerError) throws ServerException { + return initRequest(shutdownWhenServerError, null); + } + + /**初始化,加载所有请求校验配置 + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M initRequest( + APIJSONCreator creator) throws ServerException { + return initRequest(false, creator); + } + + /**初始化,加载所有请求校验配置 + * @param shutdownWhenServerError + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M initRequest( + boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { + return initRequest(shutdownWhenServerError, creator, null); + } + + /**初始化,加载所有请求校验配置 + * @param shutdownWhenServerError + * @param creator + * @param table 表内自定义数据过滤条件 + * @return + * @throws ServerException + */ + @SuppressWarnings("unchecked") + public static , L extends List> M initRequest( + boolean shutdownWhenServerError, APIJSONCreator creator, M table) throws ServerException { + if (creator == null) { + creator = (APIJSONCreator) APIJSONApplication.DEFAULT_APIJSON_CREATOR; + } + + boolean isAll = table == null || table.isEmpty(); + M tblObj = createJSONObject(); + tblObj.put(KEY_ORDER, "version-,id+"); + M requestTable = isAll ? tblObj : table; + if (Log.DEBUG == false) { + requestTable.put(APIJSONConstant.KEY_DEBUG, 0); + } + + M requestItem = JSON.createJSONObject(); + requestItem.put(REQUEST_, requestTable); // 方便查找 + requestItem.put(KEY_COUNT, 0); + + M request = JSON.createJSONObject(); + request.put(REQUEST_ + "[]", requestItem); + + M response = creator.createParser().setMethod(RequestMethod.GET).setNeedVerify(false).parseResponse(request); + if (JSONResponse.isSuccess(response) == false) { + Log.e(TAG, "\n\n\n\n\n !!!! 查询请求校验规则配置异常 !!!\n" + getString(response, JSONResponse.KEY_MSG) + "\n\n\n\n\n"); + onServerError("查询请求校验规则配置异常 !", shutdownWhenServerError); + } + + L list = getJSONArray(response, REQUEST_ + "[]"); + int size = list == null ? 0 : list.size(); + if (isAll && size <= 0) { + Log.w(TAG, "initRequest isAll && size <= 0,没有可用的请求校验规则配置"); + return response; + } + + Log.d(TAG, "initRequest < for REQUEST_MAP.size() = " + REQUEST_MAP.size() + " <<<<<<<<<<<<<<<<<<<<<<<<"); + + Map>> newMap = new LinkedHashMap<>(); + + for (int i = 0; i < size; i++) { + M item = getJSONObject(list, i); + if (item == null) { + continue; + } + + String version = getString(item, "version"); + if (StringUtil.isEmpty(version, true)) { + Log.e(TAG, "initRequest for StringUtil.isEmpty(version, true),Request 表中的 version 不能为空!"); + onServerError("服务器内部错误,Request 表中的 version 不能为空!", shutdownWhenServerError); + } + + String method = getString(item, "method"); + if (StringUtil.isEmpty(method, true)) { + Log.e(TAG, "initRequest for StringUtil.isEmpty(method, true),Request 表中的 method 不能为空!"); + onServerError("服务器内部错误,Request 表中的 method 不能为空!", shutdownWhenServerError); + } + + String tag = getString(item, "tag"); + if (StringUtil.isEmpty(tag, true)) { + Log.e(TAG, "initRequest for StringUtil.isEmpty(tag, true),Request 表中的 tag 不能为空!"); + onServerError("服务器内部错误,Request 表中的 tag 不能为空!", shutdownWhenServerError); + } + + M structure = JSON.parseObject(getString(item, "structure")); + + M target = null; + + if (structure != null) { + target = structure; + if (structure.containsKey(tag) == false) { //tag 是 Table 名或 Table[] + + boolean isArrayKey = tag.endsWith(":[]"); // apijson.isArrayKey(tag); + String key = isArrayKey ? tag.substring(0, tag.length() - 3) : tag; + + if (isTableKey(key)) { + if (isArrayKey) { //自动为 tag = Comment:[] 的 { ... } 新增键值对 "Comment[]":[] 为 { "Comment[]":[], ... } + target.put(key + "[]", JSON.createJSONArray()); + } else { //自动为 tag = Comment 的 { ... } 包一层为 { "Comment": { ... } } + target = JSON.createJSONObject(); + target.put(tag, structure); + } + } + } + } + + if (target == null || target.isEmpty()) { + Log.e(TAG, "initRequest for target == null || target.isEmpty()"); + onServerError("服务器内部错误,Request 表中的 version = " + version + ", method = " + method + ", tag = " + tag + " 对应的 structure 不能为空!", shutdownWhenServerError); + } + + String cacheKey = getCacheKeyForRequest(method, tag); + SortedMap> versionedMap = newMap.get(cacheKey); + if (versionedMap == null) { + versionedMap = new TreeMap<>(new Comparator() { + + @Override + public int compare(Integer o1, Integer o2) { + return o2 == null ? -1 : o2.compareTo(o1); // 降序 + } + }); + } + versionedMap.put(Integer.valueOf(version), item); + newMap.put(cacheKey, versionedMap); + } + + if (isAll) { // 全量更新 + REQUEST_MAP = new LinkedHashMap<>(); + REQUEST_MAP.putAll(newMap); + } else { + REQUEST_MAP.putAll(newMap); + } + + Log.d(TAG, "initRequest for /> REQUEST_MAP.size() = " + REQUEST_MAP.size() + " >>>>>>>>>>>>>>>>>>>>>>>"); + + return response; + } + + + + /**初始化,加载所有请求校验配置 + * @return + * @throws ServerException + */ + public static > M initDocument() throws ServerException { + return initDocument(false); + } + /**初始化,加载所有请求校验配置 + * @param shutdownWhenServerError + * @return + * @throws ServerException + */ + public static > M initDocument(boolean shutdownWhenServerError) throws ServerException { + return initDocument(shutdownWhenServerError, null); + } + /**初始化,加载所有请求校验配置 + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M initDocument(APIJSONCreator creator) throws ServerException { + return initDocument(false, creator); + } + /**初始化,加载所有请求校验配置 + * @param shutdownWhenServerError + * @param creator + * @return + * @throws ServerException + */ + public static , L extends List> M initDocument( + boolean shutdownWhenServerError, APIJSONCreator creator) throws ServerException { + return initDocument(shutdownWhenServerError, creator, null); + } + /**初始化,加载所有请求校验配置 + * @param shutdownWhenServerError + * @param creator + * @param table 表内自定义数据过滤条件 + * @return + * @throws ServerException + */ + @SuppressWarnings("unchecked") + public static , L extends List> M initDocument( + boolean shutdownWhenServerError, APIJSONCreator creator, M table) throws ServerException { + if (creator == null) { + creator = (APIJSONCreator) APIJSONApplication.DEFAULT_APIJSON_CREATOR; + } + + boolean isAll = table == null || table.isEmpty(); + M document = isAll ? JSON.createJSONObject(new JSONRequest("apijson{}", "length(apijson)>0").setOrder("version-,id+")) : table; + if (Log.DEBUG == false) { + document.put(APIJSONConstant.KEY_DEBUG, 0); + } + + M requestItem = JSON.createJSONObject(); + requestItem.put(DOCUMENT_, document); // 方便查找 + requestItem.put(KEY_COUNT, 0); + + M request = JSON.createJSONObject(); + request.put(DOCUMENT_ + "[]", requestItem); + + M response = creator.createParser().setMethod(RequestMethod.GET).setNeedVerify(false).parseResponse(request); + if (JSONResponse.isSuccess(response) == false) { + Log.e(TAG, "\n\n\n\n\n !!!! 查询请求映射配置异常 !!!\n" + getString(response, JSONResponse.KEY_MSG) + "\n\n\n\n\n"); + onServerError("查询请求映射配置异常 !", shutdownWhenServerError); + } + + L list = getJSONArray(response, DOCUMENT_ + "[]"); + int size = list == null ? 0 : list.size(); + if (isAll && size <= 0) { + Log.w(TAG, "initDocument isAll && size <= 0,没有可用的请求映射配置"); + return response; + } + + Log.d(TAG, "initDocument < for DOCUMENT_MAP.size() = " + DOCUMENT_MAP.size() + " <<<<<<<<<<<<<<<<<<<<<<<<"); + + + Map>> newMap = new LinkedHashMap<>(); + + for (int i = 0; i < size; i++) { + M item = getJSONObject(list, i); + if (item == null) { + continue; + } + + String version = getString(item, "version"); + if (StringUtil.isEmpty(version, true)) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + + " 对应 version 不能为空!", shutdownWhenServerError); + } + + String url = getString(item, "url"); + int index = url == null ? -1 : url.indexOf("/"); + if (index != 0) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应 url 值错误,必须以 / 开头!", shutdownWhenServerError); + } + + String requestStr = getString(item, "request"); + + String apijson = getString(item, "apijson"); + if (StringUtil.isEmpty(apijson)) { + if (StringUtil.isBranchUrl(url) == false) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应 url 值错误!只允许合法的 URL 格式!", shutdownWhenServerError); + } + } + else { + if (StringUtil.isNotEmpty(requestStr)) { + try { + JSON.parseObject(requestStr); + } + catch (Exception e) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应 request 值 " + requestStr + " 错误!只允许合法的 M 格式!" + e.getMessage(), shutdownWhenServerError); + } + } + + try { + JSON.parseObject(apijson); + } + catch (Exception e) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应 apijson 值 " + apijson + " 错误!只允许合法的 M 格式!" + e.getMessage(), shutdownWhenServerError); + } + + index = url.lastIndexOf("/"); + String method = index < 0 ? null : url.substring(0, index); + String tag = index < 0 ? null : url.substring(index + 1); + + index = method == null ? -1 : method.lastIndexOf("/"); + method = index < 0 ? method : method.substring(index + 1); + + if (METHODS.contains(method) == false) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应路径 /{method}/{tag} 中 method 值 " + method + " 错误!apijson 字段不为空时只允许 " + METHODS + " 中的一个!", shutdownWhenServerError); + } + + if (StringUtil.isName(tag) == false) { + onServerError("服务器内部错误,Document 表中的 id=" + getString(item, "id") + ", name=" + getString(item, "name") + ", url=" + url + + " 对应路径 /{method}/{tag} 中 tag 值 " + tag + " 错误!apijson 字段不为空时只允许变量命名格式!", shutdownWhenServerError); + } + + String cacheKey = getCacheKeyForRequest(method, tag); + SortedMap> versionedMap = newMap.get(cacheKey); + if (versionedMap == null) { + versionedMap = new TreeMap<>(new Comparator() { + + @Override + public int compare(Integer o1, Integer o2) { + return o2 == null ? -1 : o2.compareTo(o1); // 降序 + } + }); + } + versionedMap.put(Integer.valueOf(version), item); + newMap.put(cacheKey, versionedMap); + } + + } + + if (isAll) { // 全量更新 + DOCUMENT_MAP = newMap; + } + else { + DOCUMENT_MAP.putAll(newMap); + } + + Log.d(TAG, "initDocument for /> DOCUMENT_MAP.size() = " + DOCUMENT_MAP.size() + " >>>>>>>>>>>>>>>>>>>>>>>"); + + return response; + } + + + public static void test() throws Exception { + testStructure(); + } + + static final String requestConfig = "{\"Comment\":{\"REFUSE\": \"id\", \"MUST\": \"userId,momentId,content\"}, \"INSERT\":{\"@role\":\"OWNER\"}}"; + static final String responseConfig = "{\"User\":{\"REMOVE\": \"phone\", \"REPLACE\":{\"sex\":2}, \"INSERT\":{\"name\":\"api\"}, \"UPDATE\":{\"verifyURLList-()\":\"verifyURLList(pictureList)\"}}}"; + + /** + * 测试 Request 和 Response 的数据结构校验 + * @throws Exception + */ + public static , L extends List> void testStructure() throws Exception { + Parser parser = APIJSONApplication.createParser(); + + M request; + try { + request = JSON.parseObject("{\"Comment\":{\"userId\":0}}"); + Log.d(TAG, "test verifyRequest = " + AbstractVerifier.verifyRequest(RequestMethod.POST, "", JSON.parseObject(requestConfig), request, parser)); + } catch (Throwable e) { + if (e instanceof IllegalArgumentException == false || "POST请求,Comment 里面不能缺少 momentId 等[userId,momentId,content]内的任何字段!".equals(e.getMessage()) == false) { + throw e; + } + Log.d(TAG, "测试 Operation.MUST 校验缺少字段:成功"); + } + try { + request = JSON.parseObject("{\"Comment\":{\"id\":0, \"userId\":0, \"momentId\":0, \"content\":\"apijson\"}}"); + Log.d(TAG, "test verifyRequest = " + AbstractVerifier.verifyRequest(RequestMethod.POST, "", JSON.parseObject(requestConfig), request, parser)); + } catch (Throwable e) { + if (e instanceof IllegalArgumentException == false || "POST请求,/Comment 不能传 id !".equals(e.getMessage()) == false) { + throw e; + } + Log.d(TAG, "测试 Operation.REFUSE 校验不允许传字段:成功"); + } + try { + request = JSON.parseObject("{\"Comment\":{\"userId\":0, \"momentId\":0, \"content\":\"apijson\"}}"); + Log.d(TAG, "test verifyRequest = " + AbstractVerifier.verifyRequest(RequestMethod.POST, "", JSON.parseObject(requestConfig), request, parser)); + AssertUtil.assertEqual("OWNER", getString(request, "@role")); + Log.d(TAG, "测试 Operation.INSERT 不存在字段时插入:成功"); + } catch (Throwable e) { + throw e; + } + + + M response; + try { + response = JSON.parseObject("{\"User\":{\"userId\":0}}"); + Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, parser, null)); + AssertUtil.assertEqual("verifyURLList(pictureList)", getJSONObject(response, "User").get("verifyURLList-()")); + Log.d(TAG, "测试 Operation.UPDATE 强制插入/替换:成功"); + } catch (Throwable e) { + throw e; + } + try { + response = JSON.parseObject("{\"User\":{\"userId\":0, \"phone\":\"12345678\"}}"); + Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, parser, null)); + AssertUtil.assertEqual(null, getJSONObject(response, "User").get("phone")); + Log.d(TAG, "测试 Operation.REMOVE 强制移除:成功"); + } catch (Throwable e) { + throw e; + } + try { + response = JSON.parseObject("{\"User\":{\"userId\":0, \"phone\":\"12345678\", \"sex\":1}}"); + Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, parser, null)); + AssertUtil.assertEqual("api", getJSONObject(response, "User").get("name")); + Log.d(TAG, "测试 Operation.INSERT 不存在字段时插入:成功"); + } catch (Throwable e) { + throw e; + } + try { + response = JSON.parseObject("{\"User\":{\"id\":0, \"name\":\"tommy\", \"phone\":\"12345678\", \"sex\":1}}"); + Log.d(TAG, "test verifyResponse = " + AbstractVerifier.verifyResponse(RequestMethod.GET, "", JSON.parseObject(responseConfig), response, parser, null)); + AssertUtil.assertEqual(2, getJSONObject(response, "User").get("sex")); + Log.d(TAG, "测试 Operation.REPLACE 存在字段时替换:成功"); + } catch (Throwable e) { + throw e; + } + + } + + + protected static void onServerError(String msg, boolean shutdown) throws ServerException { + Log.e(TAG, "\n校验配置测试未通过!\n请修改 Access/Request 表里的记录!\n保证所有配置都是正确的!!!\n\n原因:\n" + msg); + + if (shutdown) { + System.exit(1); + } else { + throw new ServerException(msg); + } + } + + protected Parser parser; + @Override + public Parser getParser() { + if (parser == null) { + parser = createParser(); + } + return parser; + } + + @Override + public APIJSONVerifier setParser(AbstractParser parser) { + this.parser = parser; + return this; + } + + @SuppressWarnings("unchecked") + @NotNull + @Override + public APIJSONParser createParser() { + APIJSONParser parser = APIJSONApplication.createParser(); + parser.setVisitor(visitor); + return parser; + } + + /**登录校验 + * @author + * @modifier Lemon + * @param session + * @throws Exception + */ + public static void verifyLogin(HttpSession session) throws Exception { + Log.d(TAG, "verifyLogin session.getId() = " + (session == null ? null : session.getId())); + APIJSONApplication.createVerifier().setVisitor(getVisitor(session)).verifyLogin(); + } + + + /**获取来访用户的id + * @author Lemon + * @param session + * @return + */ + @SuppressWarnings("unchecked") + public static T getVisitorId(HttpSession session) { + if (session == null) { + return null; + } + + T id = (T) session.getAttribute(VISITOR_ID); + if (id == null) { + id = (T) getVisitor(session); + session.setAttribute(VISITOR_ID, id); + } + return id; + } + + /**获取来访用户 + * @param session + * @return + */ + @SuppressWarnings("unchecked") + public static Visitor getVisitor(HttpSession session) { + return session == null ? null : (Visitor) session.getAttribute(VISITOR_); + } + + + @Override + public String getIdKey(String database, String schema, String datasource, String table) { + return APIJSONSQLConfig.SIMPLE_CALLBACK.getIdKey(database, schema, datasource, table); + } + + @Override + public String getUserIdKey(String database, String schema, String datasource, String table) { + return APIJSONSQLConfig.SIMPLE_CALLBACK.getUserIdKey(database, schema, datasource, table); + } + + @SuppressWarnings("unchecked") + @Override + public T newId(RequestMethod method, String database, String schema, String datasource, String table) { + return (T) APIJSONSQLConfig.SIMPLE_CALLBACK.newId(method, database, schema, datasource, table); + } + +} diff --git a/src/main/java/apijson/framework/javax/AssertUtil.java b/src/main/java/apijson/framework/javax/AssertUtil.java new file mode 100644 index 0000000..a34b979 --- /dev/null +++ b/src/main/java/apijson/framework/javax/AssertUtil.java @@ -0,0 +1,21 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + + +/**简单断言工具类,不用额外引入 JUnit 等库 + * @author Lemon + */ +public class AssertUtil extends apijson.framework.AssertUtil {} diff --git a/src/main/java/apijson/framework/javax/BaseModel.java b/src/main/java/apijson/framework/javax/BaseModel.java new file mode 100755 index 0000000..e44a1a4 --- /dev/null +++ b/src/main/java/apijson/framework/javax/BaseModel.java @@ -0,0 +1,21 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +/**base model for reduce model codes + * @author Lemon + * @use extends BaseModel + */ +public abstract class BaseModel extends apijson.framework.BaseModel {} diff --git a/src/main/java/apijson/framework/javax/ColumnUtil.java b/src/main/java/apijson/framework/javax/ColumnUtil.java new file mode 100644 index 0000000..e07cf59 --- /dev/null +++ b/src/main/java/apijson/framework/javax/ColumnUtil.java @@ -0,0 +1,31 @@ +/*Copyright ©2016 APIJSON(https://github.com/APIJSON) + +Licensed 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 apijson.framework.javax; + +import apijson.RequestMethod; +import apijson.StringUtil; +import apijson.orm.AbstractSQLConfig; +import apijson.orm.AbstractSQLExecutor; + +import java.util.*; +import java.util.Map.Entry; + + +/**表字段相关工具类 + * @author Lemon + * @see 先提前配置 {@link #VERSIONED_TABLE_COLUMN_MAP}, {@link #VERSIONED_KEY_COLUMN_MAP} 等,然后调用相关方法。 + * 不支持直接关联 database, schema, datasource,可以把这些与 table 拼接为一个字符串传给参数 table,格式可以是 database-schema-datasource-table + */ +public class ColumnUtil extends apijson.framework.ColumnUtil {} diff --git a/src/main/java/apijson/framework/javax/package-info.java b/src/main/java/apijson/framework/javax/package-info.java new file mode 100755 index 0000000..ffec0a6 --- /dev/null +++ b/src/main/java/apijson/framework/javax/package-info.java @@ -0,0 +1,8 @@ +/** + * javax 包,兼容 JDK 1.8~16,使用 javax.servlet + */ +/** + * @author Lemon + * + */ +package apijson.framework.javax; \ No newline at end of file