diff --git a/.gitignore b/.gitignore index c2065bc..e45ddb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ HELP.md .gradle build/ +target/ + !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ 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/jitpack.yml b/jitpack.yml deleted file mode 100644 index 9e42c42..0000000 --- a/jitpack.yml +++ /dev/null @@ -1,6 +0,0 @@ -jdk: - - openjdk17 - -before_install: - - sdk install java 17.0.6-open - - sdk use java 17.0.6-open diff --git a/pom.xml b/pom.xml index 112b8f2..32eb7cc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ apijson.framework apijson-framework - 7.0.3 + 7.2.2 jar APIJSONFramework @@ -14,43 +14,39 @@ UTF-8 UTF-8 - 17 + 1.8 UTF-8 - 17 - 17 + jakarta.servlet jakarta.servlet-api - 6.0.0 + 5.0.0 provided + - com.alibaba - fastjson - 1.2.83 + javax.servlet + javax.servlet-api + 4.0.1 + provided com.github.Tencent APIJSON - 7.0.3 - - - com.github.APIJSON - apijson-column - 2.0.3 + 8.0.2 com.mysql mysql-connector-j - 8.4.0 + 9.2.0 org.postgresql @@ -59,20 +55,6 @@ - - - - com.github.TommyLemon - unitauto-java - 3.0.5 - - - com.github.TommyLemon - unitauto-jar - 3.0.5 - - - @@ -82,8 +64,8 @@ maven-compiler-plugin 3.12.1 - 17 - 17 + 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 567ffd5..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 jakarta.servlet.AsyncContext; -import jakarta.servlet.ServletResponse; -import jakarta.servlet.http.HttpServletRequest; 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,52 +35,198 @@ *
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 request @@ -118,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 + " 中的一个!") + )); } /**获取 @@ -192,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 @@ -207,8 +328,8 @@ public String crud(String request, HttpSession session) { //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - - + + /**增删改查统一入口,这个一个方法可替代以下 7 个方法,牺牲一些路由解析性能来提升一点开发效率 * @param method * @param tag @@ -221,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 @@ -234,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); // } /**获取 @@ -246,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 @@ -307,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)); + } + } //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -317,63 +661,63 @@ public String deleteByTag(String tag, Map params, String 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)); } } @@ -382,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); //用户 @@ -412,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 3af0e1d..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 jakarta.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,166 +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; - } - - - /**获取方法的定义 - * @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"); - } - /**获取方法的定义 - * @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 e35627b..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,11 +15,11 @@ package apijson.framework; import java.util.List; +import java.util.Map; import jakarta.servlet.http.HttpSession; import apijson.orm.*; -import com.alibaba.fastjson.JSONObject; import apijson.NotNull; import apijson.RequestMethod; @@ -28,7 +28,7 @@ /**简化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 @@ -41,28 +41,28 @@ public class APIJSONObjectParser extends AbstractObjectParser< * @param isArrayMainTable * @throws Exception */ - public APIJSONObjectParser(HttpSession session, @NotNull JSONObject request, String parentPath, SQLConfig arrayConfig - , boolean isSubQuery, boolean isTable, boolean isArrayMainTable) throws Exception { - super(request, parentPath, arrayConfig, isSubQuery, isTable, isArrayMainTable); + 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(Parser 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 71d4e4b..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 jakarta.servlet.http.HttpSession; -import com.alibaba.fastjson.JSONObject; - 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 pare }.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 af4eb91..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 (SQLConfig) 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 c18b12e..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 Prep @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 1a7643f..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 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,47 +185,69 @@ public static JSONObject initAccess(boolean shutdownWhenServerError, APIJSON Map> newMap = new LinkedHashMap<>(); Map> fakeDeleteMap = new LinkedHashMap<>(); - Map newTKMap = new LinkedHashMap<>(); - Map tableSchemaMap = 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"); - String schema = item.getString("schema"); - - 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)); } } @@ -241,22 +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) { @@ -324,16 +344,16 @@ public static JSONObject initAccess(boolean shutdownWhenServerError, APIJSON 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() + " >>>>>>>>>>>>>>>>>>>>>>>"); @@ -345,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); } @@ -354,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); } @@ -363,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); } @@ -373,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); } @@ -385,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,没有可用的请求校验规则配置"); @@ -420,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); } } @@ -474,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() { @@ -489,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); } @@ -500,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(); } @@ -511,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; @@ -524,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; @@ -533,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; @@ -588,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; } @@ -606,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(); } @@ -616,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; } @@ -634,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_); } @@ -655,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 130c5d1..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. 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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy