[
  {
    "path": "CHANGELOG",
    "content": "v2.3.1\n1、将JsonUtil改为S.json\n2、sdb和mybatis的配置文件可以打包到jar中\n\nv2.3.2\n1、增加jetty端口被占用时的重试功能，默认重试将近2分钟。通过http.bind.retry=0可以关闭重试功能。\n2、sdb的xml配置文件中，dtd不再通过网络获取。\n3、去掉MD5Util和S.md5，增加S.hasher\n4、去掉EncryUtil，增加S.encryptor\n\nv2.3.3\n1、增加redis的存活检测，在redis无法连接的时候，快速失败。\n2、增加工具类S.beans的方法\n\nv2.3.4\n1、增加RedisConfig工具类\n2、因为Observer接口从java9开始被标注为不推荐使用。所以AppInfo中使用Consumer代替Observer接口\n3、增加SimpleLogger接口和SimpleLoggerHolder类。这个是配置等跟日志同级的模块使用的\n4、允许soa和web使用自定义的线程池\n5、重构主键生成的代码\n6、支持在微服务中配置随机端口。端口值为0表示随机端口\n7、修复鹰眼跟踪的bug\n8、将分布式锁的入口改到S类中\n\nv2.3.5\n1、修复日志级别的一个bug\n2、修改分布式锁的类名称\n\nv2.3.6\n1、修复orm事件的bug\n\nv2.3.7\n1、限制rest接口只支持get和post，其中get方法仅用于测试，它有可能使用浏览器缓存\n2、将Base64Util改为S.base64，并且修改了工具类S中许多工具的名称\n3、ThreadContext改名为ActionContext\n\nv2.3.8\n1、去掉对dbcp2连接池的硬编码依赖\n2、几个名称微调\n3、将日志的代码行参数默认值改为true\n\nv2.3.9\n1、AbstractSessionServlet改名为AbstractLoginServlet，并且添加了一个方法\n2、整理日志的模块名\n3、去掉Log.printStack(Throwable e)方法\n4、Transaction的NORMAL改名为REQUIRED，EMBED改名为REQUIRES_NEW\n\nv2.4.0\n1、在配置文件中，soa开头的改为sumk.rpc开头。http开头的改为sumk.http开头\n2、将RedisCallBack等单方法的接口改为java自带的接口\n3、md5的结果从原来的大写方式改为小写方式\n\nv2.4.1\n1、将Class.newInstance()改为使用构造器初始化，这是因为java12开始，该方法被列为不推荐使用\n2、http和微服务的名称支持逗号分隔\n3、日志level的分隔符，从原来的逗号扩展到逗号和分号都可以\n\nv2.4.2\n1、S.base64增加2个小方法\n2、SystemConfig.keys()的返回值，从Collection改为Set\n\nv2.5.0\n1、ORM去掉了withnull属性，改为自动判断。如果开发者传递的参数是pojo对象，会将pojo中null字段排除掉，如果传递的是map，其中的null字段会被保留。比如作为where条件，它将会作为IS NULL条件\n2、ORM中的update去掉根据redis主键更新的方法\n3、S.bean去掉无参的beanToMap方法。\n4、Plugin接口的stop方法提供默认方法。\n5、HttpUtil增加获取request和response的方法\n6、优化字节码处理\n\nv2.5.1\n1、优化分布式session的存取性能\n2、去掉http对redis的显式依赖\n3、去掉session中对象的修改功能\n4、去掉session根据不同客户端类型定制超时时间的功能\n\nv2.5.2\n1、去掉sumk.log.type参数，如果要切换日志类型需要手工调用api（这个是冷门需求）\n2、将app.properties的获取机制简单化\n\nv2.5.3\n1、将HttpUtil改名为WebUtil\n2、允许动态调整核心线程池的容量\n3、去掉SimpleBizException类，将BizException的构造方法改为私有，改为使用create方法创建\n\nv2.6.0\n1、重构数据库连接的获取方式，增加读连接的升级功能\n2、提供默认的微服务日志\n\nv2.6.1\n1、优化微服务的日志\n2、数据库连接AutoCommit的控制从连接池改为框架内置控制\n3、去掉了Cached注解及相关功能\n\nv2.6.2\n1、按参数顺序调用的Rpc接口，允许接口发布后再添加参数\n2、限制日志body的长度，默认1000，后面用省略号替代\n\nv2.6.3\n1、SumkDate的毫秒时间，允许末尾的0省略掉(如果全是0，保留1个就行)\n2、系统启动的时候，将原来一些同步启动的方法改为异步多线程启动，加快启动速度\n3、改进本机ip的获取方式\n\nv2.6.4\n1、Select增加不等于比较符\n\nv2.6.5\n1、 select增加对IS NOT NULL的支持\n2、修改web和rpc拦截器接口的定义\n3、添加web是否支持get请求的开关\n4、将后台任务线程改为正常模式，然后去除了SumkServer.main()的无限阻塞\n5、添加web接口名是否大小写敏感的开关，默认铭感。\n\nv2.6.6\n1、优化http的日志功能\n\nv2.6.7\n1、http允许开发者指定默认接口，如果接口找不到就会进入该接口\n2、允许通过sumk.redis.disconnect.null配置，指定在redis服务器异常的时候，是抛异常还是返回null\n3、通过sumk.redis.conf.**来配置redis连接池的默认属性\n\nv2.7.0\n1、精简redis接口，并去掉与jedis包的直接耦合\n2、修改redis的配置方式\n3、优化日志级别判定的性能\n4、对依赖包的版本做了一次集体升级\n\nv2.7.1\n1、修复sumk-log与spring boot、阿波罗配置中心一起使用时，spring日志的堆栈溢出问题\n\nv2.7.2\n1、在2.7.0中，将jedis升级到2.10.x后，之前版本的jedis需要手工构建JedisPool对象。现修复这个问题\n2、@Box事务支持AutoCommit模式\n3、只是Pojo使用byte[]和String映射Blob和Clob类型\n4、修改@Column、@Param的一些属性名称\n\nv2.7.3\n1、增加工具类M，一般用于消息提示，通过与AppInfo的搭配，也可以实现国际化的目的\n2、增加@RequestBody注解，用于支持http的流传输\n3、去掉了http的客户类型功能（即@Web的type），因为这个功能感觉很鸡肋\n4、增加类似dubbo那种，直接用接口注册和调用的微服务。目前对泛型返回值支持不好，如有需要，需借助JsonTypes工具\n\n\nv2.7.5\n1、可以统一对http服务的出错信息进行定制\n2、当s.log.xxx的配置只有一个路径时，path:这个前缀可以省略\n3、当web服务没有定义入参的时候，可以通过HttpServletRequest的InputStream来读取输入流\n\nv.2.7.6\n1、将工具类S的属性改为方法\n2、AES默认加密方式改为CBC\n3、默认的配置管理增加对System参数的读取，但不监听System中参数的变更\n\nv2.7.7\n1、新增@CreateTime注解，它的作用是新增记录的时候，会自动传入创建时间，并且与id保持一致\n2、统一日志独立为特殊日志，并且有纯配置方式改为代码与配置相结合的方式\n3、优化了日志输出的方式\n4、优化了作业调度并提供Task类\n5、redis支持原生cluster模式\n6、去掉了groupId。appId使用分隔符方式可以起到groupId一样的效果\n\nv2.7.8\n1、增加慢sql的耗时日志\n\nv2.7.9\n1、SDB的xml增加items标签\n2、SDB增加builder方法\n\nv2.8.0\n1、rpc的协议做了变更，不能与2.8之前的微服务进行交互\n2、优化SumkDate性能\n3、WebFileter和RpcFilter调用下一个Filter方法改为callNextFilter\n4、Redis增加mute()方法，调用之后，如果发生异常，会用null来代替抛出异常\n\nv2.8.1\n1、文件上传改为基于servlet3的MultiPart模式，而不是commons-upload。同时去掉对commons-upload的依赖。相应的，WebUtil的getUploadFiles()改名为getMultiParts，UploadFile类改为MultipartItem\n2、StreamUtil的extractData改名为readAllBytes\n\nv2.8.2\n1、@Web的requestEncrypt改为requestType,responseEncrypt改为responseType。对应的EncryptType类型改名为MessageType，NONE属性改名为PLAIN，AES改名为ENCRYPT\n2、去掉@ErrorHandler，这个用法较冷门，通过WebFilter接口也能轻松实现\n3、LoginObject的error方法改名为fail\n4、提供加密接口的明文调试功能\n\nv2.8.3\n1、sumk.http.fusing支持头尾*匹配，并且放宽对接口书写格式的要求\n2、移除CollectionUtil.unmodifyList()方法移除第二个参数\n3、SDB的selectOne改名为queryOne\n4、@CreateTime改名为@AutoCreateTime，并且无论是否主键是自动生成的，该注解都生效\n5、Select的byPrimaryId方法改名为byDatabaseId\n6、DBType和TransactionType改了下包名位置\n7、web响应增加s-trace头部，它用于跟踪日志\n\nv2.8.4\n1、统一日志增加sumk.unionlog.exclude参数，并修复异常信息打印的问题\n2、增加SumkExceptionCode接口，用于定义一些重要的框架异常码，方便开发者提示错误信息\n\nv2.8.5\n1、s.log.day等日志配置增加exclude过滤条件\n2、@Param参数增加对空字符串的过滤\n\nv2.9.0\n1、zk和http方式的统一配置支持多节点模式\n2、http错误码由原来的499改为550\n3、个别地方性能优化\n\nv2.9.1\n1、DB部分更新支持字段为null\n2、支持设置用户session最长的存活时间\n\nv2.9.2\n1、登录时的加密key改为通过header传递\n2、修复Select中缓存跟数据库数据重复的bug\n3、lock增加动态设置锁超时的功能\n4、@Web添加tags属性 \n\nv2.9.3\n1、SumkDate支持在DB和SDB中使用，但不支持mbatis、hibernate等外置数据库操作\n2、支持使用配置的方式替代@SoaClass注解\n3、S.bean()支持日期类型、数字类型的模糊匹配\n4、@Web添加method属性\n5、SumkServer.main增加class参数，以支持sumk.jar和工程代码不在一个classloader里\n\nv2.9.4\n1、@Param支持pojo以及数组方式\n2、BizException允许通过配置输出堆栈\n3、WebUtil和RpcUtil提供所有参数组成的map，并且map的遍历顺序与参数顺序一致\n4、通过sumk.db.default可以配置默认的数据库名称\n5、修改SDB的包名\n\nv2.9.5\n1、SDB的count改为返回long\n2、s.http.response.header.开头的配置会被添加到web的响应header中\n\nv2.9.6\n1、将@Web的requireLogin默认值改为true，但是默认禁用，要配置sumk.http.login.enable=1才能启用\n\nv2.9.7\n1、StreamUtil改名为IOUtil\n2、@Inject去掉beanClz()和handler()\n3、接口文档增加一级属性的模糊搜索功能，比如name用_name=XX来搜索\n4、微服务增加发送失败自动重发的机制\n5、修改StartConstants和StartContext的包名\n\nv2.9.8\n1、增加@ExcludeInParams和@ExcludeInResponse，它用来标识dto中非参数字段或不是用于返回的字段。在web和rpc中都有效\n2、允许pojo中存在跟@SoftDelete同名的字段，以达到复用软删除标识位的目的。这种情况下，往往只有一种状态是标识删除，其它状态都是非删除的\n3、去掉sumk.db.default参数，增加s.alias.db.{name}参数\n4、BeanPropertiesWatcher接口改名为BeanInjectWatcher\n\nv2.9.9\n1、支持对配置中的redis密码进行加密\n2、http接口支持名称相同，但是http方法不同的情况，并且支持接口的前缀匹配。对于前缀匹配的情况，WebUtil.getUrlLeft()可以获取剩下的url内容\n3、SumkException的状态码全部改为负数，以跟BizException进行区分\n4、将redis和数据库的初始化移到Plugin的prepare阶段，同时去掉Plugins类。修改之后，startAsyn阶段的数据库和redis都已经准备好了，不需要再用Plugins来判断\n\nv2.9.10\n1、对于常见的注解，使用相应Spec将它包装起来，以支持自定义注解。同时移除HttpActionNode的web()方法，upload()也改为返回UploadSpec\n\nv2.9.11\n1、接口方式的rpc调用，如果返回值是泛型，不再需要手工调用JsonTypes进行注册\n2、去掉@Param的custom属性，但是保留ParamSpec的custom属性，开发者可以通过这里的custom进行扩展\n\nv2.9.12\n1、添加BootWatcher接口，该接口允许在IOC启动前做一些操作\n2、DB添加commit和rollback两个hook\n3、增加对数据源的操作统计\n\nv2.10.0\n1、使用字节码等方式替换原有的http、rpc入参解析方式，减少反射引起动态生成class类\n2、CalleeNode中的部分以arg开头的方法，改名为param开头，去掉isEmptyArgument方法，增加paramLength方法\n3、微服务支持sumk.zkurl不配置（服务端和客户端都支持），这时候的rpc只能调用本进程里的微服务接口\n4、DB钩子（hook）增加ON_COMMIT和CLOSED两种，并且入参从Runable改为Consumer\n5、增加sumk.db.name.lowercase参数，用于将默认的表名和字段名改为小写，自定义名称不受本参数影响。这个原先需要通过注入自定义的实现类来实现\n6、数据库selec类增加in方法，并且允许同一种比较出现两次，比如： name != '张三' and name != '李四'\n\nv2.10.1\n1、Select增加notIn方法，用于sql中的not in()\n2、简单的in查询、selectColumns指定查询列也都能使用缓存了\n3、RecordRepository的包名改为org.yx.db.visit\n4、除了默认的redis缓存外，还允许自定义缓存实现类\n5、修改AbstractCachable的cacheEnable方法，这个只在自动生成的代码(sumk-codetool)里使用\n6、@Bean增加辅助注解@ConditionOnPropert，支持组合关系：并且（用,或&&分隔)以及或者(用||分隔)。其中onMatch属性用来做取反操作\n7、改进SumkDate转String的性能\n\nv2.10.2\n1、Select的offset、limit增加最大值1万的限制。可以通过代码突破这个限制。这意味着开发人员知道该语句比较耗性能\n2、细节优化以提升性能或降低内存使用。比如用List或数组替代map，将一些计数器从Atomic类型改为普通类型等\n3、提供监听器功能。监听器实现SumkListener接口，然后使用相应的EventBus发布事件。sumk-server-demo工程中InfoListener是它的使用例子\n4、去掉DBEventListener接口，使用SumkListener改写数据库事件监听。sumk-server-demo工程中TableListener是它的使用例子\n5、@Inject增加value属性，该属性强制指定注入bean的名称\n6、去掉RedisUtil工具类(基本也用不到)，增加Redis2接口\n\nv3.0.0\n1、Redis2接口改为Redis3x，并增加增加发布订阅方法。依赖的jedis从2.x升级到3.7.0\n2、监听器增加异步功能，同时支持异步发布事件以及异步监听两种模式\n3、配置文件的sumk.jetty开头的配置改为sumk.webserver开头\n4、列表为基础的Map替代HashMap作为小Map使用，以压缩内存的的占用。从SBuilder.listMap()可以获取到\n5、slf4j支持\\\\{}转义\n6、除data外，其它http rest请求的paramter名称都在前面加_，比如sign改为_sign，这些参数不常用\n7、重构rpc的序列化协议，并把mina改为netty。mina的支持移到sumk-rpc-mina项目中\n8、升级了许多依赖包的版本，这些依赖包用原来的版本也是兼容的\n\n与2.x不兼容的地方\n1、两者的rpc协议不同，不能互相通信\n2、http的sign、act、plainKey参数前面增加了_\n3、Redis2接口改为Redis3x\n\nv3.0.1\n1、去掉Attachable接口，RpcUtil.attachments()改为RpcUtil\n\nv3.1.0\n最主要的是将rpc透传数据的最大长度从255提升到60K，这个造成微服务跟旧版不兼容(无透传时仍然可以调用)\n1、@Inject增加allowMulti属性，允许存在多个bean的情况下，取第一个注入\n2、Select增加and方法，可以处理or表达式，也可以处理自定义的比较表达式\n3、去掉Select的addEquals方法（此方法意义不大，增加理解成本），开发者通过多次调用addEqual来实现\n4、优化DB代码，减少中间过程内存的使用量，并优化sql语句中的空格。不等号统一改为<>\n\nv3.1.1\n1、UserSession的loadUserObject()改名为loadAndRefresh()，这个方法开发者一般也很少用到\n2、增加Const.LISTENER_DB_MODIFY_ON_COMMIT类型监听器，它在事务提交前监听，能够操作数据库\n3、ModifyEvent可以获取被影响条数，并且支持boolean类型的传参\n\nv3.1.2\n1、增加BeanProvider接口，IOC通过BeanProvider获取bean。方便与Spring等第三方IOC对接\n\nv3.2.0\n1、异常码从int类型改为String类型（int方式入参仍保留）。旧用户在客户端升级前，可通过sumk.http.interrorcode=1让返回值使用int方式\n2、允许自定义数据库事务的实现\n3、BizException的堆栈信息改为默认打印，可以通过sumk.bizexception.fullstack=0来关闭\n\nv4.0.0\n1、增加aop拦截器\n2、将sumk工程拆分为base、framewrok、db、http、rpc五个工程，加上早就分出去的async-logger、sumk-rpc-mina，总共7个\n\nv4.0.1\n1、取消SumkException的code为负数的限制，并且将内置的负数改为正数\n\nv4.0.2\n1、新增对国际化的支持，主要类是I18n，并去掉工具类M。web和rpc对locale的解析和设置需要自己通过filter去实现\n2、优化日志实现\n\nv4.1.0\n1、框架初始化改为多线程并行初始化，加快启动速度\n2、支持自定义注册中心，内置注册中心还是zk\n3、修改内置的分布式主键生成器，旧项目请在配置文件中添加sumk.seq.version=1\n\nv4.2.0\n1、添加sumk.factories，提供更加丰富的扩展支持\n2、支持SumkServer.start(xx.class,xx)的方式扫描包路径,不需要显示配置sumk.ioc\n3、zookeeper和jetty的依赖包版本升级\n4、app.properties支持行尾\\作为跨行连接符，这个跟Properties标准文件一致。它们最大的区别在于app.properties不支持转义字符\n\nv4.2.1\n1、slf4j支持2.x版本"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\r\n                           Version 2.0, January 2004\r\n                        http://www.apache.org/licenses/\r\n\r\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r\n\r\n   1. Definitions.\r\n\r\n      \"License\" shall mean the terms and conditions for use, reproduction,\r\n      and distribution as defined by Sections 1 through 9 of this document.\r\n\r\n      \"Licensor\" shall mean the copyright owner or entity authorized by\r\n      the copyright owner that is granting the License.\r\n\r\n      \"Legal Entity\" shall mean the union of the acting entity and all\r\n      other entities that control, are controlled by, or are under common\r\n      control with that entity. For the purposes of this definition,\r\n      \"control\" means (i) the power, direct or indirect, to cause the\r\n      direction or management of such entity, whether by contract or\r\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\r\n      outstanding shares, or (iii) beneficial ownership of such entity.\r\n\r\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\r\n      exercising permissions granted by this License.\r\n\r\n      \"Source\" form shall mean the preferred form for making modifications,\r\n      including but not limited to software source code, documentation\r\n      source, and configuration files.\r\n\r\n      \"Object\" form shall mean any form resulting from mechanical\r\n      transformation or translation of a Source form, including but\r\n      not limited to compiled object code, generated documentation,\r\n      and conversions to other media types.\r\n\r\n      \"Work\" shall mean the work of authorship, whether in Source or\r\n      Object form, made available under the License, as indicated by a\r\n      copyright notice that is included in or attached to the work\r\n      (an example is provided in the Appendix below).\r\n\r\n      \"Derivative Works\" shall mean any work, whether in Source or Object\r\n      form, that is based on (or derived from) the Work and for which the\r\n      editorial revisions, annotations, elaborations, or other modifications\r\n      represent, as a whole, an original work of authorship. For the purposes\r\n      of this License, Derivative Works shall not include works that remain\r\n      separable from, or merely link (or bind by name) to the interfaces of,\r\n      the Work and Derivative Works thereof.\r\n\r\n      \"Contribution\" shall mean any work of authorship, including\r\n      the original version of the Work and any modifications or additions\r\n      to that Work or Derivative Works thereof, that is intentionally\r\n      submitted to Licensor for inclusion in the Work by the copyright owner\r\n      or by an individual or Legal Entity authorized to submit on behalf of\r\n      the copyright owner. For the purposes of this definition, \"submitted\"\r\n      means any form of electronic, verbal, or written communication sent\r\n      to the Licensor or its representatives, including but not limited to\r\n      communication on electronic mailing lists, source code control systems,\r\n      and issue tracking systems that are managed by, or on behalf of, the\r\n      Licensor for the purpose of discussing and improving the Work, but\r\n      excluding communication that is conspicuously marked or otherwise\r\n      designated in writing by the copyright owner as \"Not a Contribution.\"\r\n\r\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\r\n      on behalf of whom a Contribution has been received by Licensor and\r\n      subsequently incorporated within the Work.\r\n\r\n   2. Grant of Copyright License. Subject to the terms and conditions of\r\n      this License, each Contributor hereby grants to You a perpetual,\r\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n      copyright license to reproduce, prepare Derivative Works of,\r\n      publicly display, publicly perform, sublicense, and distribute the\r\n      Work and such Derivative Works in Source or Object form.\r\n\r\n   3. Grant of Patent License. Subject to the terms and conditions of\r\n      this License, each Contributor hereby grants to You a perpetual,\r\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n      (except as stated in this section) patent license to make, have made,\r\n      use, offer to sell, sell, import, and otherwise transfer the Work,\r\n      where such license applies only to those patent claims licensable\r\n      by such Contributor that are necessarily infringed by their\r\n      Contribution(s) alone or by combination of their Contribution(s)\r\n      with the Work to which such Contribution(s) was submitted. If You\r\n      institute patent litigation against any entity (including a\r\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\r\n      or a Contribution incorporated within the Work constitutes direct\r\n      or contributory patent infringement, then any patent licenses\r\n      granted to You under this License for that Work shall terminate\r\n      as of the date such litigation is filed.\r\n\r\n   4. Redistribution. You may reproduce and distribute copies of the\r\n      Work or Derivative Works thereof in any medium, with or without\r\n      modifications, and in Source or Object form, provided that You\r\n      meet the following conditions:\r\n\r\n      (a) You must give any other recipients of the Work or\r\n          Derivative Works a copy of this License; and\r\n\r\n      (b) You must cause any modified files to carry prominent notices\r\n          stating that You changed the files; and\r\n\r\n      (c) You must retain, in the Source form of any Derivative Works\r\n          that You distribute, all copyright, patent, trademark, and\r\n          attribution notices from the Source form of the Work,\r\n          excluding those notices that do not pertain to any part of\r\n          the Derivative Works; and\r\n\r\n      (d) If the Work includes a \"NOTICE\" text file as part of its\r\n          distribution, then any Derivative Works that You distribute must\r\n          include a readable copy of the attribution notices contained\r\n          within such NOTICE file, excluding those notices that do not\r\n          pertain to any part of the Derivative Works, in at least one\r\n          of the following places: within a NOTICE text file distributed\r\n          as part of the Derivative Works; within the Source form or\r\n          documentation, if provided along with the Derivative Works; or,\r\n          within a display generated by the Derivative Works, if and\r\n          wherever such third-party notices normally appear. The contents\r\n          of the NOTICE file are for informational purposes only and\r\n          do not modify the License. You may add Your own attribution\r\n          notices within Derivative Works that You distribute, alongside\r\n          or as an addendum to the NOTICE text from the Work, provided\r\n          that such additional attribution notices cannot be construed\r\n          as modifying the License.\r\n\r\n      You may add Your own copyright statement to Your modifications and\r\n      may provide additional or different license terms and conditions\r\n      for use, reproduction, or distribution of Your modifications, or\r\n      for any such Derivative Works as a whole, provided Your use,\r\n      reproduction, and distribution of the Work otherwise complies with\r\n      the conditions stated in this License.\r\n\r\n   5. Submission of Contributions. Unless You explicitly state otherwise,\r\n      any Contribution intentionally submitted for inclusion in the Work\r\n      by You to the Licensor shall be under the terms and conditions of\r\n      this License, without any additional terms or conditions.\r\n      Notwithstanding the above, nothing herein shall supersede or modify\r\n      the terms of any separate license agreement you may have executed\r\n      with Licensor regarding such Contributions.\r\n\r\n   6. Trademarks. This License does not grant permission to use the trade\r\n      names, trademarks, service marks, or product names of the Licensor,\r\n      except as required for reasonable and customary use in describing the\r\n      origin of the Work and reproducing the content of the NOTICE file.\r\n\r\n   7. Disclaimer of Warranty. Unless required by applicable law or\r\n      agreed to in writing, Licensor provides the Work (and each\r\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\r\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r\n      implied, including, without limitation, any warranties or conditions\r\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r\n      PARTICULAR PURPOSE. You are solely responsible for determining the\r\n      appropriateness of using or redistributing the Work and assume any\r\n      risks associated with Your exercise of permissions under this License.\r\n\r\n   8. Limitation of Liability. In no event and under no legal theory,\r\n      whether in tort (including negligence), contract, or otherwise,\r\n      unless required by applicable law (such as deliberate and grossly\r\n      negligent acts) or agreed to in writing, shall any Contributor be\r\n      liable to You for damages, including any direct, indirect, special,\r\n      incidental, or consequential damages of any character arising as a\r\n      result of this License or out of the use or inability to use the\r\n      Work (including but not limited to damages for loss of goodwill,\r\n      work stoppage, computer failure or malfunction, or any and all\r\n      other commercial damages or losses), even if such Contributor\r\n      has been advised of the possibility of such damages.\r\n\r\n   9. Accepting Warranty or Additional Liability. While redistributing\r\n      the Work or Derivative Works thereof, You may choose to offer,\r\n      and charge a fee for, acceptance of support, warranty, indemnity,\r\n      or other liability obligations and/or rights consistent with this\r\n      License. However, in accepting such obligations, You may act only\r\n      on Your own behalf and on Your sole responsibility, not on behalf\r\n      of any other Contributor, and only if You agree to indemnify,\r\n      defend, and hold each Contributor harmless for any liability\r\n      incurred by, or claims asserted against, such Contributor by reason\r\n      of your accepting any such warranty or additional liability.\r\n\r\n   END OF TERMS AND CONDITIONS\r\n\r\n   APPENDIX: How to apply the Apache License to your work.\r\n\r\n      To apply the Apache License to your work, attach the following\r\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\r\n      replaced with your own identifying information. (Don't include\r\n      the brackets!)  The text should be enclosed in the appropriate\r\n      comment syntax for the file format. We also recommend that a\r\n      file or class name and description of purpose be included on the\r\n      same \"printed page\" as the copyright notice for easier\r\n      identification within third-party archives.\r\n\r\n   Copyright youtongluan (\\u6e38\\u901a\\u92ae\\uff0c\\u522b\\u540d\\uff1a\\u6e38\\u590f)\r\n\r\n   Licensed under the Apache License, Version 2.0 (the \"License\");\r\n   you may not use this file except in compliance with the License.\r\n   You may obtain a copy of the License at\r\n\r\n       http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n   Unless required by applicable law or agreed to in writing, software\r\n   distributed under the License is distributed on an \"AS IS\" BASIS,\r\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n   See the License for the specific language governing permissions and\r\n   limitations under the License.\r\n"
  },
  {
    "path": "README.md",
    "content": "# sumk\r\n\r\nsumk是为互联网而生的，在性能、分布式、扩展性等方面考虑较多，互联网常见的特性很多都内置支持，比如数据库读写分离、调用链跟踪、统一日志等，与spring体系相比，sumk更轻量、性能更高、内存消耗更低。以下是主要工程介绍，具体功能参见[sumk的功能特性](https://p2nwdvhb36.feishu.cn/docx/LQxXdjwbdoWDrFxcyUTcNWTUnSh)\r\n\r\n- sumk-base：配置管理和日志接口\r\n\r\n- sumk-framework：核心框架，主要是IOC\r\n\r\n- sumk-db：数据库功能，包括事务、ORM等\r\n\r\n- sumk-http：mvc组件\r\n\r\n- Sumk-rpc：微服务功能\r\n\r\n- sumk-rpc-mina：可选组件，如果引入该组件，rpc底层就会使用mina替代netty\r\n\r\n- async-logger（又名sumk-log)：slf4j日志的实现，同时支持鹰眼跟踪和统一日志。\r\n  \r\n  \r\n\r\n### 功能介绍\r\n\r\n[sumk总体介绍](https://p2nwdvhb36.feishu.cn/docx/AEIhdF4M5oDXouxdfNLc0ya2nZb)\r\n\r\n[sumk功能特性](https://p2nwdvhb36.feishu.cn/docx/LQxXdjwbdoWDrFxcyUTcNWTUnSh)\r\n\r\n### 使用\r\n\r\n[sumk框架入门](https://p2nwdvhb36.feishu.cn/docx/AOl0dhDqJoymnSxuWUhcTQ1SnMf)\r\n\r\n[注解及接口及工具类](https://p2nwdvhb36.feishu.cn/docx/UuIPduSDuo6kSlxSOEDcGzdPnSh)\r\n\r\n[sumk常用配置](https://p2nwdvhb36.feishu.cn/docx/RUBidOGQboZTkaxGJ8uc1Z93n8c)\r\n\r\n[sumk-db使用介绍](https://p2nwdvhb36.feishu.cn/docx/TQnUdmM1YomahpxVIKMcxPdYnFc)\r\n\r\n[接口文档及状态信息查看](https://p2nwdvhb36.feishu.cn/docx/ZvV3dCbLuog5wfxoSAgcV6frnvc)\r\n\r\n### 示例工程\r\n\r\n<https://github.com/youtongluan/sumk-server-demo>\r\n\r\n### \r\n\r\n### 作者：游夏。QQ：3205207767\r\n"
  },
  {
    "path": "async-logger/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright youtongluan (\\u6e38\\u901a\\u92ae\\uff0c\\u522b\\u540d\\uff1a\\u6e38\\u590f)\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "async-logger/README.md",
    "content": "# sumk-log\n&emsp;&emsp;async-logger是一款基于slf4j标准的日志系统。它的特点是高效、异步、支持配置中心统一配置，拥有鹰眼跟踪与统一日志等功能。它的前身名叫sumk-log\n\n### 引入sumk-log.jar\n```\n<dependency>\n    <groupId>com.github.youtongluan</groupId>\n    <artifactId>async-logger</artifactId>\n    <version>3.2.0</version>\n</dependency>\n```\n最新版本请查看maven中央库。\n\n### 配置说明（大多数配置都不需要重启系统）：\n\n\n#### sumk.log.level=info,XX:debug,X.X:error\n前面表示名称，后面是日志级别。如果没有名称,就表示是全局的。默认级别是INFO\n\n#### sumk.log.console=1 启用控制台输出\n\n#### s.log.日志类型=path:日志存放路径;module:com.test,a.*;exclude:b.*\n* 日志类型现有day、month、level三种，支持扩展。统一日志的配置不在这里。以day日志为例，它可以简化为s.log.day=/logs/app-#.log\n* 日志存放路径中要有一个#,比如/log/daylog-#.log\n* module指定该日志所对应的日志名。支持头尾出现*作为通配符。这个配置是可选的\n* exclude用于过滤掉不需要输出的日志。支持头尾出现*作为通配符。这个配置是可选的\n* level类型日志跟day类型相似，差异是level日志除了module外，还增加了个level属性，它可以设置只输出摸个级别以上的日志\n\n### 统一日志\n* 统一日志系统跟普通类型有点不同。它需要调用UnionLogs.start()来开启。\n* 通过sumk.unionlog.module 配置统一日志允许打印的模块。\n* 通过UnionLogs.getUnionLog().directOffer()方式输出的日志，不受module配置影响，无法关闭。这是因为统一日志需要具备审查的功能，不允许开发者随意关闭，所以采用代码和配置相结合的方式\n* 统一日志默认也是输出到文件，可以自己定义输出位置。参见test目录下的UnionDemo\n\n### 其它参数\n* sumk.log.day.max day或level类型日志的日志文件保存个数，默认30\n* sumk.log.month.max month类型的日志的日志文件保存个数，默认2\n* 日志体达到某种大小后会被截短，这些参数的设置，参见[入门教程](https://github.com/youtongluan/sumk-server-demo)里的《sumk框架入门.docx》\n\n\n#### 其它说明\n* 将这个特性与统一配置相结合，就可以动态切换日志级别，也可以动态设置是否将某一类型日志发送到日志中心（统一日志）。\n* sumk日志跟logback一个很大的不同在于，sumk日志的level是全局的，而logback的level是针对具体日志类型的。\n\n\n"
  },
  {
    "path": "async-logger/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.github.youtongluan</groupId>\n\t\t<artifactId>sumk</artifactId>\n\t\t<version>4.2.1</version>\n\t</parent>\n\t<artifactId>async-logger</artifactId>\n\t<name>com.github.youtongluan:sumk</name>\n\t<description>A quick developing framewort for internet company</description>\n\t<url>https://github.com/youtongluan/sumk</url>\n\t<licenses>\n\t\t<license>\n\t\t\t<name>The Apache License, Version 2.0</name>\n\t\t\t<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n\t\t</license>\n\t</licenses>\n\t<scm>\n\t\t<url>https://github.com/youtongluan/sumk</url>\n\t\t<connection>https://github.com/youtongluan/sumk.git</connection>\n\t\t<developerConnection>https://github.com/youtongluan/sumk</developerConnection>\n\t</scm>\n\t<distributionManagement>\n\t    <repository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n\t    </repository>\n\t    <snapshotRepository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n\t    </snapshotRepository>\n\t</distributionManagement>\n\t\n\t<developers>\n\t\t<developer>\n\t\t\t<name>youtongluan</name>\n\t\t\t<email>3205207767@qq.com</email>\n\t\t\t<url>https://www.oschina.net/p/sumk-log</url>\n\t\t</developer>\n\t</developers>\n\t\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>sumk-base</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t    <groupId>org.slf4j</groupId>\n\t\t    <artifactId>slf4j-api</artifactId>\n\t\t</dependency>\n\t\t<!-- 用于log4j 2.x 到 slf4j的转化 -->\n\t\t<dependency>\n\t\t    <groupId>org.apache.logging.log4j</groupId>\n\t\t    <artifactId>log4j-to-slf4j</artifactId>\n\t\t    <version>2.14.1</version>\n\t\t    <optional>true</optional>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t</dependencies>\n</project>"
  },
  {
    "path": "async-logger/src/main/java/org/slf4j/impl/StaticLoggerBinder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.slf4j.impl;\n\nimport org.slf4j.ILoggerFactory;\nimport org.slf4j.spi.LoggerFactoryBinder;\nimport org.yx.log.impl.SumkLoggerFactory;\n\npublic class StaticLoggerBinder implements LoggerFactoryBinder {\n\n\tprivate static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();\n\n\tpublic static final StaticLoggerBinder getSingleton() {\n\t\treturn SINGLETON;\n\t}\n\n\tpublic static String REQUESTED_API_VERSION = \"1.7.0\";\n\n\tprivate final ILoggerFactory loggerFactory;\n\n\tprivate StaticLoggerBinder() {\n\t\tloggerFactory = new SumkLoggerFactory();\n\t}\n\n\tpublic ILoggerFactory getLoggerFactory() {\n\t\treturn loggerFactory;\n\t}\n\n\tpublic String getLoggerFactoryClassStr() {\n\t\treturn SumkLoggerFactory.class.getName();\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/slf4j/impl/StaticMDCBinder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.slf4j.impl;\n\nimport org.slf4j.helpers.NOPMDCAdapter;\nimport org.slf4j.spi.MDCAdapter;\n\npublic class StaticMDCBinder {\n\tpublic static final StaticMDCBinder SINGLETON = new StaticMDCBinder();\n\n\tprivate StaticMDCBinder() {\n\t}\n\n\tpublic MDCAdapter getMDCA() {\n\t\treturn new NOPMDCAdapter();\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/slf4j/v2/SumkServiceProvider.java",
    "content": "package org.slf4j.v2;\n\nimport org.slf4j.ILoggerFactory;\nimport org.slf4j.IMarkerFactory;\nimport org.slf4j.helpers.BasicMDCAdapter;\nimport org.slf4j.helpers.BasicMarkerFactory;\nimport org.slf4j.spi.MDCAdapter;\nimport org.slf4j.spi.SLF4JServiceProvider;\nimport org.yx.log.impl.SumkLoggerFactory;\n\npublic class SumkServiceProvider implements SLF4JServiceProvider {\n\n\tprivate ILoggerFactory loggerFactory;\n\n\tprivate IMarkerFactory markerFactory;\n\n\tprivate MDCAdapter mdcAdapter;\n\n\t@Override\n\tpublic ILoggerFactory getLoggerFactory() {\n\t\treturn loggerFactory;\n\t}\n\n\t@Override\n\tpublic IMarkerFactory getMarkerFactory() {\n\t\treturn markerFactory;\n\t}\n\n\t@Override\n\tpublic MDCAdapter getMDCAdapter() {\n\t\treturn mdcAdapter;\n\t}\n\n\t@Override\n\tpublic String getRequestedApiVersion() {\n\t\treturn \"2.0.1\";\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tthis.loggerFactory = new SumkLoggerFactory();\n\t\tthis.markerFactory = new BasicMarkerFactory();\n\t\tthis.mdcAdapter = new BasicMDCAdapter();\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/CodeLine.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\npublic class CodeLine {\n\tfinal String className;\n\tfinal String methodName;\n\tfinal int lineNumber;\n\n\tpublic CodeLine(String className, String methodName, int lineNumber) {\n\t\tthis.className = className;\n\t\tthis.methodName = methodName;\n\t\tthis.lineNumber = lineNumber;\n\t}\n\n\tpublic String getClassName() {\n\t\treturn className;\n\t}\n\n\tpublic String getMethodName() {\n\t\treturn methodName;\n\t}\n\n\tpublic int getLineNumber() {\n\t\treturn lineNumber;\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/CodeLineKit.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.Objects;\nimport java.util.function.BiFunction;\n\nimport org.slf4j.Marker;\nimport org.yx.log.CodeLineMarker;\n\npublic final class CodeLineKit {\n\n\tprivate static BiFunction<Marker, String, CodeLine> parser = (marker, logModule) -> {\n\t\tif (marker != null && marker.getClass() == CodeLineMarker.class) {\n\t\t\treturn LogHelper.extractCodeLine(marker.getName());\n\t\t}\n\t\treturn LogHelper.extractCodeLine(\"org.yx.log.\");\n\t};\n\n\tpublic static BiFunction<Marker, String, CodeLine> getParser() {\n\t\treturn parser;\n\t}\n\n\tpublic static void setParser(BiFunction<Marker, String, CodeLine> parser) {\n\t\tCodeLineKit.parser = Objects.requireNonNull(parser);\n\t}\n\n\tpublic static CodeLine parse(Marker marker, String name) {\n\t\treturn parser.apply(marker, name);\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/DayRollingFileAppender.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.time.LocalDate;\nimport java.util.Objects;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.util.SumkDate;\n\npublic class DayRollingFileAppender extends RollingFileAppender {\n\n\tpublic DayRollingFileAppender() {\n\t\tsuper(\"day\");\n\t}\n\n\tpublic DayRollingFileAppender(String name) {\n\t\tsuper(Objects.requireNonNull(name));\n\t}\n\n\t@Override\n\tprotected boolean shouldDelete(String fileName) {\n\t\tString c = LogHelper.realContext(fileName, filePattern, SLOT);\n\t\tif (c == null || c.length() != 10) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tLocalDate logDate = LocalDate.parse(c);\n\t\t\tLocalDate now = LocalDate.now();\n\t\t\treturn logDate.isBefore(now.minusDays(AppInfo.getInt(\"sumk.log.day.max\", 30)));\n\t\t} catch (Exception e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tprotected String formatDateString(SumkDate date) {\n\t\treturn date.to_yyyy_MM_dd();\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/DefaultUnionLog.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.yx.base.matcher.Matchers;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.SystemConfig;\n\npublic class DefaultUnionLog extends LogQueue implements UnionLog {\n\n\tprivate UnionLogDao dao;\n\tprivate boolean started;\n\tprivate Consumer<SystemConfig> observer;\n\n\tprivate Function<LogObject, UnionLogObject> logObjectSerializer;\n\n\tprivate Supplier<Predicate<String>> matcherSupplier;\n\n\tpublic DefaultUnionLog() {\n\t\tsuper(\"unionlog\");\n\t\tthis.dao = new LocalFileDao();\n\t\tthis.matcherSupplier = () -> Matchers.includeAndExclude(AppInfo.get(\"sumk.unionlog.module\", null),\n\t\t\t\tAppInfo.get(\"sumk.unionlog.exclude\", null));\n\t}\n\n\t@Override\n\tprotected void flush(boolean idle) throws Exception {\n\t\tthis.dao.flush(idle);\n\t}\n\n\t@Override\n\tprotected void output(List<LogObject> list) throws Exception {\n\t\tList<UnionLogObject> logs = new ArrayList<>(list.size());\n\t\tfor (LogObject raw : list) {\n\t\t\tUnionLogObject log = this.logObjectSerializer.apply(raw);\n\t\t\tif (log == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlogs.add(log);\n\t\t}\n\t\tif (logs.size() > 0) {\n\t\t\tthis.dao.store(logs);\n\t\t}\n\t}\n\n\t@Override\n\tprotected boolean onStart(Map<String, String> configMap) {\n\t\tif (this.logObjectSerializer == null) {\n\t\t\tthis.logObjectSerializer = getLogObjectSerializer();\n\t\t}\n\t\tif (this.observer == null) {\n\t\t\tthis.observer = c -> setMatcher(matcherSupplier.get());\n\t\t\tAppInfo.addObserver(this.observer);\n\t\t}\n\t\tthis.started = true;\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic synchronized void stop() throws Exception {\n\t\tthis.started = false;\n\t\tsuper.stop();\n\n\t}\n\n\t@Override\n\tpublic boolean directOffer(LogObject logObject) {\n\t\tif (!this.started) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.queue.offer(logObject);\n\t}\n\n\t@Override\n\tpublic boolean offer(LogObject logObject) {\n\t\tif (!this.started) {\n\t\t\treturn false;\n\t\t}\n\t\treturn super.offer(logObject);\n\t}\n\n\t@Override\n\tpublic boolean isStarted() {\n\t\treturn this.started;\n\t}\n\n\tpublic Supplier<Predicate<String>> getMatcherSupplier() {\n\t\treturn matcherSupplier;\n\t}\n\n\tpublic void setMatcherSupplier(Supplier<Predicate<String>> matcherSupplier) {\n\t\tthis.matcherSupplier = Objects.requireNonNull(matcherSupplier);\n\t\tthis.setMatcher(this.matcherSupplier.get());\n\t}\n\n\tpublic Function<LogObject, UnionLogObject> getLogObjectSerializer() {\n\t\treturn logObjectSerializer == null ? new UnionLogObjectSerializer() : this.logObjectSerializer;\n\t}\n\n\tpublic void setLogObjectSerializer(Function<LogObject, UnionLogObject> logObjectSerializer) {\n\t\tthis.logObjectSerializer = Objects.requireNonNull(logObjectSerializer);\n\t}\n\n\tpublic UnionLogDao getDao() {\n\t\treturn dao;\n\t}\n\n\tpublic void setDao(UnionLogDao dao) {\n\t\tthis.dao = Objects.requireNonNull(dao);\n\t}\n\n\tpublic void removeObserver() {\n\t\tConsumer<SystemConfig> ob = this.observer;\n\t\tif (ob == null) {\n\t\t\treturn;\n\t\t}\n\t\tAppInfo.removeObserver(ob);\n\t\tthis.observer = null;\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/LeveledDayRollingFileAppender.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.yx.log.ConsoleLog;\nimport org.yx.log.LogLevel;\n\npublic class LeveledDayRollingFileAppender extends DayRollingFileAppender {\n\n\tprivate LogLevel level = LogLevel.INFO;\n\n\tpublic LeveledDayRollingFileAppender() {\n\t\tthis(\"level\");\n\t}\n\n\tpublic LeveledDayRollingFileAppender(String name) {\n\t\tsuper(name);\n\t}\n\n\t@Override\n\tprotected boolean accept(LogObject logObject) {\n\t\treturn logObject.methodLevel.ordinal() >= level.ordinal() && super.accept(logObject);\n\t}\n\n\t@Override\n\tpublic void config(Map<String, String> configMap) {\n\t\tif (configMap == null) {\n\t\t\tconfigMap = Collections.emptyMap();\n\t\t}\n\t\tsuper.config(configMap);\n\t\tString lev = configMap.get(\"level\");\n\t\tif (lev == null || lev.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tthis.level = LogLevel.valueOf(lev.toUpperCase());\n\t\t} catch (Exception e) {\n\t\t\tConsoleLog.defaultLog.error(\"{} is not a valid level name\", lev);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/LocalFileDao.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.nio.file.StandardOpenOption;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.log.ConsoleLog;\nimport org.yx.util.UUIDSeed;\n\npublic class LocalFileDao implements UnionLogDao {\n\tprivate static final String LINE_SPLIT = \"\\n\";\n\tprivate final long MAX_FILE_LENGTH = AppInfo.getInt(\"sumk.log.union.max_file_length\", 100 * 1024 * 1024);\n\tprivate final int MAX_RECORD_SIZE = AppInfo.getInt(\"sumk.log.union.max_record_size\", 200);\n\tprivate int aliveTime = AppInfo.getInt(\"sumk.log.union.alive_time\", 15000);\n\n\tprivate long createTime = -1;\n\n\tprivate int fileLength;\n\tprivate int recordSize;\n\tprivate List<byte[]> buffer;\n\n\tpublic LocalFileDao() {\n\t\tthis.buffer = new ArrayList<>(MAX_RECORD_SIZE);\n\t}\n\n\tprivate File createLogingFile() {\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\ttry {\n\t\t\t\tString name = AppInfo.pid().concat(\"_\").concat(UUIDSeed.seq18());\n\t\t\t\tFile f = new File(getLogingPath(), name);\n\t\t\t\tif (!f.getParentFile().exists()) {\n\t\t\t\t\tFile parent = f.getParentFile();\n\t\t\t\t\tif (!parent.mkdirs()) {\n\t\t\t\t\t\tConsoleLog.defaultLog.error(\"create folder \" + parent.getAbsolutePath() + \" failed!!!\");\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!f.createNewFile()) {\n\t\t\t\t\tConsoleLog.defaultLog.error(\"create file \" + f.getAbsolutePath() + \" failed!!!\");\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn f;\n\t\t\t} catch (Exception e) {\n\t\t\t\tConsoleLog.defaultLog.error(e.getMessage(), e);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void store(List<UnionLogObject> logs) throws IOException {\n\t\tint logCount = logs.size();\n\n\t\tStringBuilder sb = new StringBuilder(600 * Math.min(10, logs.size()));\n\t\tfor (UnionLogObject log : logs) {\n\t\t\tsb.append(log.log).append(LINE_SPLIT);\n\t\t}\n\t\tbyte[] bs = sb.toString().getBytes(AppInfo.UTF8);\n\t\tsb = null;\n\t\tbuffer.add(bs);\n\t\tthis.recordSize += logCount;\n\t\tthis.fileLength += bs.length;\n\t\tif (this.recordSize >= MAX_RECORD_SIZE || this.fileLength > MAX_FILE_LENGTH) {\n\t\t\treset();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void flush(boolean idle) {\n\t\tif (this.recordSize >= MAX_RECORD_SIZE || this.fileLength > MAX_FILE_LENGTH\n\t\t\t\t|| System.currentTimeMillis() - createTime >= aliveTime) {\n\t\t\treset();\n\t\t}\n\t}\n\n\tpublic void reset() {\n\t\tList<byte[]> datas = this.buffer;\n\t\tif (datas.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tthis.buffer = new ArrayList<>();\n\t\tlong size = 0;\n\t\tFileChannel channel = null;\n\t\ttry {\n\t\t\tFile logFile = createLogingFile();\n\t\t\tchannel = FileChannel.open(logFile.toPath(), StandardOpenOption.APPEND);\n\t\t\tByteBuffer[] bufs = new ByteBuffer[datas.size()];\n\t\t\tfor (int i = 0; i < bufs.length; i++) {\n\t\t\t\tbufs[i] = ByteBuffer.wrap(datas.get(i));\n\t\t\t\tsize += datas.get(i).length;\n\t\t\t}\n\n\t\t\tdo {\n\t\t\t\tsize -= channel.write(bufs);\n\t\t\t} while (size != 0);\n\t\t\tchannel.force(true);\n\t\t\tchannel.close();\n\t\t\tchannel = null;\n\t\t\tmove2Loged(logFile);\n\t\t\tthis.fileLength = 0;\n\t\t\tthis.recordSize = 0;\n\t\t} catch (Exception e) {\n\t\t\tConsoleLog.defaultLog.error(e.toString(), e);\n\t\t} finally {\n\t\t\tif (channel != null) {\n\t\t\t\ttry {\n\t\t\t\t\tchannel.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tConsoleLog.defaultLog.error(e.toString(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void move2Loged(File logFile) {\n\t\tUnionLogUtil.move2Loged(logFile);\n\t}\n\n\tprotected File getLogingPath() {\n\t\treturn UnionLogUtil.getLogingPath();\n\t}\n\n\tpublic void setAliveTime(int time) {\n\t\tthis.aliveTime = time;\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/LogAppendObserver.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.function.Consumer;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.SystemConfig;\nimport org.yx.log.ConsoleLog;\nimport org.yx.log.LogSettings;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic class LogAppendObserver implements Consumer<SystemConfig> {\n\n\t@Override\n\tpublic void accept(SystemConfig info) {\n\t\tLogSettings.updateSettings();\n\t\tLogObject.updateCodeLineOnOff();\n\t\tMap<String, String> newAppenders = AppInfo.subMap(LogAppenders.LOG_APPENDER);\n\t\tfor (LogAppender append : LogAppenders.logAppenders) {\n\t\t\tString v = newAppenders.remove(append.name());\n\t\t\tif (v == null || v.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tappend.stop();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tConsoleLog.defaultLog.error(e.toString(), e);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tv = StringUtil.toLatin(v);\n\t\t\tMap<String, String> map = CollectionUtil.loadMapFromText(v, \";\", \":\");\n\t\t\tappend.config(map);\n\t\t}\n\n\t\tif (newAppenders.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tList<LogAppender> appends = new ArrayList<>();\n\t\tfor (LogAppender append : LogAppenders.logAppenders) {\n\t\t\tappends.add(append);\n\t\t}\n\t\tif (LogAppenders.isStarted()) {\n\t\t\tConsoleLog.defaultLog.info(\"find new appends:{}\", newAppenders);\n\t\t}\n\t\tfor (Entry<String, String> entry : newAppenders.entrySet()) {\n\t\t\tString k = entry.getKey();\n\t\t\tString p = entry.getValue();\n\t\t\tLogAppender appender = LogAppenders.startAppender(k, p);\n\t\t\tif (appender != null) {\n\t\t\t\tappends.add(appender);\n\t\t\t}\n\n\t\t}\n\t\tLogAppenders.logAppenders = appends.toArray(new LogAppender[appends.size()]);\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/LogAppender.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.Map;\n\npublic interface LogAppender {\n\n\tboolean start(Map<String, String> configMap);\n\n\tvoid stop() throws Exception;\n\n\tboolean offer(LogObject logObject);\n\n\tString name();\n\n\tvoid config(Map<String, String> configMap);\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/LogAppenderFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.yx.log.ConsoleLog;\n\npublic final class LogAppenderFactory {\n\n\tprivate static final ConcurrentMap<String, LogAppender> map = new ConcurrentHashMap<>();\n\n\tstatic synchronized void init() throws Exception {\n\t\tCollection<LogAppender> appenders = Arrays.asList(new LeveledDayRollingFileAppender(),\n\t\t\t\tnew MonthRollingFileAppender(), new DayRollingFileAppender());\n\t\tfor (LogAppender append : appenders) {\n\t\t\tmap.put(append.name(), append);\n\t\t}\n\t}\n\n\tstatic LogAppender start(String name, Map<String, String> configMap) throws Exception {\n\t\tLogAppender append = map.get(name);\n\t\tif (append == null) {\n\t\t\tConsoleLog.defaultLog.error(\"{} cannot find appender\", name);\n\t\t\treturn null;\n\t\t}\n\t\tif (!append.start(configMap)) {\n\t\t\tConsoleLog.defaultLog.error(\"{} started failed,value is {}\", name, configMap);\n\t\t\treturn null;\n\t\t}\n\t\treturn append;\n\t}\n\n\tpublic static LogAppender registeAppender(LogAppender m) {\n\t\tObjects.requireNonNull(m);\n\t\treturn map.put(m.name(), m);\n\t}\n\n\tpublic static void remove(String name) {\n\t\tif (name == null || name.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tmap.remove(name);\n\t}\n\n\tpublic static LogAppender getAppender(String name) {\n\t\treturn map.get(name);\n\t}\n\n\tpublic static Set<String> appenderNames() {\n\t\treturn Collections.unmodifiableSet(map.keySet());\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/LogAppenders.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic class LogAppenders {\n\tstatic final String LOG_APPENDER = \"s.log.\";\n\tpublic static final String MODULE = \"module\";\n\tpublic static final String PATH = \"path\";\n\tstatic LogAppender[] logAppenders = new LogAppender[0];\n\tprivate static boolean started;\n\n\tstatic LogAppender startAppender(String name, String value) {\n\t\tif (value == null || value.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tvalue = StringUtil.toLatin(value);\n\t\ttry {\n\t\t\treturn LogAppenderFactory.start(name, CollectionUtil.loadMapFromText(value, \";\", \":\"));\n\t\t} catch (Throwable e) {\n\t\t\tSystem.err.println(\"appender [\" + name + \"] = \" + value + \" create failed\");\n\t\t\te.printStackTrace();\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tstatic synchronized void init() {\n\t\tif (started) {\n\t\t\treturn;\n\t\t}\n\t\tstarted = true;\n\t\ttry {\n\t\t\tLogAppenderFactory.init();\n\t\t\tAppInfo.addObserver(new LogAppendObserver());\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tpublic static boolean offer(LogObject logObject) {\n\t\tboolean output = false;\n\t\tfor (LogAppender log : logAppenders) {\n\t\t\tif (log.offer(logObject)) {\n\t\t\t\toutput = true;\n\t\t\t}\n\t\t}\n\t\tif (UnionLogs.getUnionLog().offer(logObject)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn output;\n\t}\n\n\tpublic static boolean isStarted() {\n\t\treturn started;\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/LogHelper.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.Objects;\n\nimport org.yx.log.ConsoleLog;\n\npublic class LogHelper {\n\n\tprivate static PlainOutput output = new PlainOutputImpl();\n\n\tpublic static void setOutput(PlainOutput output) {\n\t\tLogHelper.output = Objects.requireNonNull(output);\n\t}\n\n\tpublic static String plainMessage(LogObject logObject, boolean showAttachs) {\n\t\treturn output.plainMessage(logObject, showAttachs);\n\t}\n\n\tpublic static void plainMessage(StringBuilder sb, LogObject logObject, boolean showAttachs) {\n\t\toutput.plainMessage(sb, logObject, showAttachs);\n\t}\n\n\tpublic static CodeLine extractCodeLine(String pre) {\n\t\tif (pre == null || pre.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tStackTraceElement stack[] = (new Throwable()).getStackTrace();\n\t\tif (stack != null && stack.length > 2) {\n\t\t\tint i = stack.length - 1;\n\n\t\t\tfor (; i > 0; i--) {\n\t\t\t\tif (stack[i].getClassName().startsWith(pre)) {\n\t\t\t\t\ti++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tStackTraceElement s = stack[i];\n\n\t\t\tif (s.getClassName().contains(\".sumkbox.\") && (++i) < stack.length) {\n\t\t\t\ts = stack[i];\n\t\t\t}\n\t\t\treturn new CodeLine(s.getClassName(), s.getMethodName(), s.getLineNumber());\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static String realContext(String text, String pattern, String slot) {\n\t\tString[] fs = pattern.split(slot, 2);\n\t\tif (fs.length != 2) {\n\t\t\tConsoleLog.defaultLog.error(\"{} should contain and only contain one {}\", pattern, slot);\n\t\t\treturn null;\n\t\t}\n\t\tif (fs[0].length() + fs[1].length() > text.length()) {\n\t\t\treturn null;\n\t\t}\n\t\tint end = text.length() - fs[1].length();\n\t\tif (!fs[0].equals(text.substring(0, fs[0].length())) || !fs[1].equals(text.substring(end))) {\n\t\t\treturn null;\n\t\t}\n\t\treturn text.substring(fs[0].length(), end);\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/LogObject.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Map;\n\nimport org.slf4j.Marker;\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.base.context.ActionContext;\nimport org.yx.base.context.LogContext;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.LogKits;\nimport org.yx.log.LogLevel;\nimport org.yx.log.LogSettings;\nimport org.yx.log.SumkLogger;\nimport org.yx.util.SumkDate;\n\npublic class LogObject {\n\tpublic static final char LN = '\\n';\n\tpublic static final Charset CHARSET = StandardCharsets.UTF_8;\n\tprivate static boolean codelineEnable = true;\n\n\t@NotNull\n\tfinal SumkDate logDate;\n\n\t@NotNull\n\tfinal LogLevel methodLevel;\n\n\tfinal String body;\n\n\t@NotNull\n\tfinal String threadName;\n\n\tfinal Throwable exception;\n\n\t@NotNull\n\tfinal String loggerName;\n\n\tfinal CodeLine codeLine;\n\n\tfinal LogContext logContext;\n\n\tpublic static LogObject create(Marker marker, LogLevel methodLevel, String message, Throwable e,\n\t\t\tSumkLogger logger) {\n\n\t\tString loggerName = logger.getName();\n\t\tCodeLine codeLine = null;\n\t\tif (codelineEnable && (!loggerName.startsWith(\"sumk.\") || marker != null)) {\n\t\t\tcodeLine = CodeLineKit.parse(marker, loggerName);\n\t\t}\n\t\treturn new LogObject(loggerName, SumkDate.now(), methodLevel,\n\t\t\t\tLogKits.shorterSubfix(message, LogSettings.maxBodyLength()), e, Thread.currentThread().getName(),\n\t\t\t\tActionContext.current().logContext(), codeLine);\n\t}\n\n\tpublic LogObject(@NotNull String loggerName, @NotNull SumkDate logDate, @NotNull LogLevel methodLevel, String body,\n\t\t\tThrowable exception, @NotNull String threadName, LogContext logContext, CodeLine codeLine) {\n\t\tthis.logDate = logDate;\n\t\tthis.methodLevel = methodLevel;\n\t\tthis.body = body;\n\t\tthis.exception = exception;\n\t\tthis.threadName = threadName;\n\t\tthis.logContext = logContext != null ? logContext : LogContext.empty();\n\t\tthis.loggerName = loggerName;\n\t\tthis.codeLine = codeLine;\n\t}\n\n\tpublic String spanId() {\n\t\treturn logContext.spanId;\n\t}\n\n\tpublic String traceId() {\n\t\treturn logContext.traceId;\n\t}\n\n\tpublic String userId() {\n\t\treturn logContext.userId;\n\t}\n\n\tpublic boolean isTest() {\n\t\treturn logContext.test;\n\t}\n\n\tpublic Map<String, String> attachments() {\n\t\treturn logContext.unmodifiedAttachs();\n\t}\n\n\tpublic SumkDate getLogDate() {\n\t\treturn logDate;\n\t}\n\n\tpublic LogLevel getMethodLevel() {\n\t\treturn methodLevel;\n\t}\n\n\tpublic String getBody() {\n\t\treturn body;\n\t}\n\n\tpublic String getThreadName() {\n\t\treturn threadName;\n\t}\n\n\tpublic Throwable getException() {\n\t\treturn exception;\n\t}\n\n\tpublic String getLoggerName() {\n\t\treturn this.loggerName;\n\t}\n\n\tpublic CodeLine getCodeLine() {\n\t\treturn codeLine;\n\t}\n\n\tpublic LogContext getLogContext() {\n\t\treturn logContext;\n\t}\n\n\tstatic void updateCodeLineOnOff() {\n\t\tcodelineEnable = AppInfo.getBoolean(\"sumk.log.codeline\", true);\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/LogQueue.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Predicate;\n\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.base.matcher.Matchers;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.ConsoleLog;\nimport org.yx.util.SumkThreadPool;\n\npublic abstract class LogQueue implements Runnable {\n\n\tprotected final String name;\n\n\tprivate int interval;\n\n\tprivate long handleLogCount;\n\n\tprivate boolean jobStarted;\n\n\tprotected final BlockingQueue<LogObject> queue;\n\n\tprivate Predicate<String> matcher = BooleanMatcher.FALSE;\n\n\tpublic LogQueue(String name) {\n\t\tthis.name = name;\n\t\tthis.interval = AppInfo.getInt(\"sumk.log.interval.\" + name, 1000);\n\t\tthis.queue = new LinkedBlockingQueue<>(Integer.getInteger(\"sumk.log.queue.\" + name, 2_0000));\n\t}\n\n\tprotected boolean accept(LogObject logObject) {\n\t\tString module = logObject.getLoggerName();\n\t\treturn this.matcher.test(module);\n\t}\n\n\tprotected abstract void flush(boolean idle) throws Exception;\n\n\tpublic void config(Map<String, String> configMap) {\n\t\tif (configMap == null) {\n\t\t\tconfigMap = Collections.emptyMap();\n\t\t}\n\t\tString patterns = configMap.get(LogAppenders.MODULE);\n\t\tif (patterns == null || patterns.isEmpty()) {\n\t\t\tpatterns = Matchers.WILDCARD;\n\t\t}\n\t\tthis.matcher = Matchers.includeAndExclude(patterns, configMap.get(\"exclude\"));\n\t\tConsoleLog.defaultLog.debug(\"{} set matcher ：{}\", this.name, this.matcher);\n\t}\n\n\tpublic final String name() {\n\t\treturn this.name;\n\t}\n\n\tpublic boolean offer(LogObject logObject) {\n\t\tif (!accept(logObject)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn queue.offer(logObject);\n\t}\n\n\tprotected abstract void output(List<LogObject> list) throws Exception;\n\n\t@Override\n\tpublic void run() {\n\t\tThread.currentThread().setName(\"log-\" + this.name);\n\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tthis.flush(this.consume());\n\t\t\t} catch (Throwable e) {\n\t\t\t\tConsoleLog.defaultLog.warn(\"日志消费失败，\" + e.toString(), e);\n\n\t\t\t\tif (Thread.currentThread().isInterrupted() || e.getClass() == InterruptedException.class) {\n\t\t\t\t\tConsoleLog.defaultLog.warn(\"{}日志停止了\", this.name);\n\t\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate boolean consume() throws Exception {\n\t\tLogObject message = queue.poll(interval, TimeUnit.MILLISECONDS);\n\t\tif (message == null) {\n\t\t\treturn true;\n\t\t}\n\t\tint batch = Math.min(queue.size() + 10, 100);\n\t\tList<LogObject> list = new ArrayList<>(batch);\n\t\tlist.add(message);\n\t\tqueue.drainTo(list, batch - 1);\n\n\t\twhile (list.size() > 0) {\n\t\t\toutput(list);\n\t\t\tthis.handleLogCount += list.size();\n\t\t\tlist.clear();\n\t\t\tqueue.drainTo(list, batch);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic synchronized boolean start(Map<String, String> map) {\n\t\tif (map == null) {\n\t\t\tmap = Collections.emptyMap();\n\t\t}\n\t\tif (!onStart(map)) {\n\t\t\treturn false;\n\t\t}\n\t\tConsoleLog.defaultLog.debug(\"{} started by {}\", this, map);\n\t\tif (!jobStarted) {\n\t\t\tstartJob();\n\t\t\tthis.jobStarted = true;\n\t\t}\n\t\treturn jobStarted;\n\t}\n\n\tprotected void startJob() {\n\t\tSumkThreadPool.executor().execute(this);\n\t}\n\n\tprotected abstract boolean onStart(Map<String, String> configMap);\n\n\tpublic synchronized void stop() throws Exception {\n\t\tthis.matcher = BooleanMatcher.FALSE;\n\t\tConsoleLog.defaultLog.info(\"日志{} stoped\", this.name);\n\t}\n\n\tpublic void setInterval(int interval) {\n\t\tif (interval > 0) {\n\t\t\tthis.interval = interval;\n\t\t}\n\t}\n\n\tpublic long getHandleLogCount() {\n\t\treturn handleLogCount;\n\t}\n\n\tprotected Predicate<String> getMatcher() {\n\t\treturn matcher;\n\t}\n\n\tprotected void setMatcher(Predicate<String> matcher) {\n\t\tthis.matcher = Objects.requireNonNull(matcher);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.name + \" [queue size:\" + queue.size() + \",matcher:\" + matcher + \",logCount:\" + handleLogCount + \"]\";\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/MonthRollingFileAppender.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.time.LocalDate;\nimport java.util.Objects;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.util.SumkDate;\n\npublic class MonthRollingFileAppender extends RollingFileAppender {\n\n\tpublic MonthRollingFileAppender() {\n\t\tsuper(\"month\");\n\t}\n\n\tpublic MonthRollingFileAppender(String name) {\n\t\tsuper(Objects.requireNonNull(name));\n\t}\n\n\t@Override\n\tprotected String formatDateString(SumkDate date) {\n\t\treturn date.to_yyyy_MM();\n\t}\n\n\t@Override\n\tprotected boolean shouldDelete(String fileName) {\n\t\tString c = LogHelper.realContext(fileName, filePattern, SLOT);\n\t\tif (c == null || c.length() != 7) {\n\t\t\treturn false;\n\t\t}\n\t\tc += \"-01\";\n\t\ttry {\n\t\t\tLocalDate logDate = LocalDate.parse(c);\n\t\t\tLocalDate now = LocalDate.now().withDayOfMonth(1);\n\n\t\t\treturn logDate.isBefore(now.minusMonths(AppInfo.getInt(\"sumk.log.month.max\", 2)));\n\t\t} catch (Exception e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/PlainOutput.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\npublic interface PlainOutput {\n\n\tString plainMessage(LogObject logObject, boolean showAttachs);\n\n\tvoid plainMessage(StringBuilder sb, LogObject logObject, boolean showAttachs);\n\n\tvoid setShowSN(boolean showSN);\n\n\tvoid setShowThreadName(boolean showThreadName);\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/PlainOutputImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.yx.base.context.ActionContext;\nimport org.yx.log.LogKits;\nimport org.yx.log.LogSettings;\nimport org.yx.util.ExceptionUtil;\n\npublic class PlainOutputImpl implements PlainOutput {\n\tprivate boolean showThreadName = true;\n\tprivate boolean showSN = true;\n\n\t@Override\n\tpublic void setShowThreadName(boolean showThreadName) {\n\t\tthis.showThreadName = showThreadName;\n\t}\n\n\t@Override\n\tpublic void setShowSN(boolean showSN) {\n\t\tthis.showSN = showSN;\n\t}\n\n\tpublic void plainMessage(StringBuilder sb, LogObject logObject, boolean showAttachs) {\n\t\tthis.appendHeader(sb, logObject).append(logObject.methodLevel).append(' ');\n\t\tif (logObject.codeLine != null) {\n\t\t\tString clzShortName = LogKits.shorterPrefix(logObject.codeLine.className, LogSettings.maxLogNameLength());\n\n\t\t\tif (!Objects.equals(logObject.getLoggerName(), logObject.codeLine.className)) {\n\t\t\t\tsb.append('(').append(logObject.getLoggerName()).append(')');\n\t\t\t}\n\t\t\tsb.append(clzShortName).append(':').append(logObject.codeLine.lineNumber);\n\t\t} else {\n\t\t\tsb.append(logObject.getLoggerName());\n\t\t}\n\t\tMap<String, String> attachs = logObject.attachments();\n\t\tif (showAttachs && attachs != null) {\n\t\t\tsb.append(\" #\").append(attachs);\n\t\t}\n\n\t\tsb.append(\" - \").append(logObject.body).append(LogObject.LN);\n\t\tif (logObject.exception != null) {\n\t\t\tExceptionUtil.printStackTrace(sb, logObject.exception);\n\t\t\tsb.append(LogObject.LN);\n\t\t}\n\t}\n\n\tprotected StringBuilder appendHeader(StringBuilder sb, LogObject logObject) {\n\t\tStringBuilder sn = new StringBuilder();\n\t\tif (logObject.userId() != null) {\n\t\t\tsn.append(logObject.userId());\n\t\t}\n\t\tif (logObject.traceId() != null) {\n\t\t\tif (sn.length() > 0) {\n\t\t\t\tsn.append('@');\n\t\t\t}\n\t\t\tsn.append(logObject.traceId());\n\t\t\tif (logObject.spanId() != null) {\n\t\t\t\tsn.append('-').append(logObject.spanId());\n\t\t\t}\n\t\t}\n\t\tsb.append(logObject.logDate.to_yyyy_MM_dd_HH_mm_ss_SSS());\n\t\tif (ActionContext.current().isTest()) {\n\t\t\tsb.append(\" -TEST- \");\n\t\t}\n\t\tif (showThreadName) {\n\t\t\tsb.append(\" [\").append(logObject.threadName).append(\"] \");\n\t\t}\n\t\tif (showSN && sn.length() > 0) {\n\t\t\tsb.append(\" {\").append(sn).append(\"} \");\n\t\t}\n\t\treturn sb;\n\t}\n\n\t@Override\n\tpublic String plainMessage(LogObject logObject, boolean showAttachs) {\n\t\tStringBuilder sb = new StringBuilder(64);\n\t\tthis.plainMessage(sb, logObject, showAttachs);\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/RollingFileAppender.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.nio.file.StandardOpenOption;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.ConsoleLog;\nimport org.yx.log.LogSettings;\nimport org.yx.util.StringUtil;\nimport org.yx.util.SumkDate;\n\npublic abstract class RollingFileAppender extends LogQueue implements LogAppender {\n\n\tpublic static final String SLOT = \"#\";\n\tprivate static final long DAY_MILS = 1000L * 3600 * 24;\n\n\tprotected StringBuilder buffer;\n\tprotected String currentDate;\n\tprotected FileChannel channel;\n\n\tprivate boolean dirty;\n\tprivate int bufferSize = AppInfo.getInt(\"sumk.log.buffer.size\", 2048);\n\n\tprotected int syncInterval = AppInfo.getInt(\"sumk.log.sync.interval\", 3000);\n\n\tprivate long lastDeleteTime;\n\n\tprivate long lastSyncTime;\n\n\tprotected String filePattern;\n\tprotected File dir;\n\n\tprotected boolean setupFilePath(String fileName) {\n\t\tif (StringUtil.isEmpty(fileName)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (fileName.indexOf(SLOT) < 0) {\n\t\t\tConsoleLog.defaultLog.error(\"{} should contain {}\", fileName, SLOT);\n\t\t\treturn false;\n\t\t}\n\t\tif (fileName.indexOf(SLOT) != fileName.lastIndexOf(SLOT)) {\n\t\t\tConsoleLog.defaultLog.error(\"{} contain more than one {}\", fileName, SLOT);\n\t\t\treturn false;\n\t\t}\n\t\tFile file = new File(fileName);\n\t\tthis.filePattern = file.getName();\n\t\tif (!this.filePattern.contains(SLOT)) {\n\t\t\tConsoleLog.defaultLog.error(\"{} should contain {}\", this.filePattern, SLOT);\n\t\t\treturn false;\n\t\t}\n\t\tthis.dir = file.getParentFile();\n\t\tif (!this.dir.exists() && !this.dir.mkdirs()) {\n\t\t\tConsoleLog.defaultLog.error(\"directory [{}{}] is not exists, and cannot create!!!\",\n\t\t\t\t\tthis.dir.getAbsolutePath(), File.pathSeparator);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic RollingFileAppender(String name) {\n\t\tsuper(name);\n\t}\n\n\t@Override\n\tprotected void flush(boolean idle) {\n\n\t\tif (this.buffer != null && this.buffer.length() > bufferSize) {\n\t\t\tthis.buffer = null;\n\t\t}\n\t\tif (this.getMatcher() == BooleanMatcher.FALSE && this.channel != null) {\n\t\t\tthis.sync();\n\t\t\tthis.closeCurrentChannel();\n\t\t}\n\t\tlong now = System.currentTimeMillis();\n\t\tif (this.dirty && (now - this.lastSyncTime >= this.syncInterval)) {\n\t\t\tthis.lastSyncTime = now;\n\t\t\tthis.sync();\n\t\t}\n\n\t\tif (now - this.lastDeleteTime >= DAY_MILS) {\n\t\t\tthis.lastDeleteTime = now;\n\t\t\tthis.deleteHisLog();\n\t\t}\n\t}\n\n\tprotected void deleteHisLog() {\n\t\tString[] files = dir.list();\n\t\tif (files == null || files.length == 0) {\n\t\t\treturn;\n\t\t}\n\t\tfor (String f : files) {\n\t\t\tif (this.shouldDelete(f)) {\n\t\t\t\ttry {\n\t\t\t\t\tFile log = new File(dir, f);\n\t\t\t\t\tlog.delete();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tprotected void output(List<LogObject> msgs) throws IOException {\n\t\tString date = this.formatDateString(msgs.get(0).logDate);\n\t\tint fromIndex = 0;\n\t\tfor (int i = 1; i < msgs.size(); i++) {\n\t\t\tLogObject obj = msgs.get(i);\n\t\t\tString d2 = this.formatDateString(obj.logDate);\n\t\t\tif (!d2.equals(date)) {\n\t\t\t\tthis.outputInSameDate(date, msgs.subList(fromIndex, i));\n\t\t\t\tdate = d2;\n\t\t\t\tfromIndex = i;\n\t\t\t}\n\t\t}\n\t\tif (fromIndex == 0) {\n\t\t\tthis.outputInSameDate(date, msgs);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.outputInSameDate(date, msgs.subList(fromIndex, msgs.size()));\n\t}\n\n\tprotected void outputInSameDate(String date, List<LogObject> msgs) throws IOException {\n\t\tif (!date.equals(this.currentDate)) {\n\t\t\tif (this.channel != null) {\n\t\t\t\tthis.sync();\n\t\t\t\tthis.closeCurrentChannel();\n\t\t\t}\n\t\t\tFile file = new File(this.dir, filePattern.replace(SLOT, date));\n\t\t\tif (!file.exists() && !file.createNewFile()) {\n\t\t\t\tConsoleLog.defaultLog.error(\"{} create fail \", file.getAbsolutePath());\n\t\t\t\tfor (LogObject logObject : msgs) {\n\t\t\t\t\tSystem.err.print(LogHelper.plainMessage(logObject, LogSettings.showAttach()));\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.channel = FileChannel.open(file.toPath(), StandardOpenOption.APPEND);\n\t\t\tthis.currentDate = date;\n\t\t}\n\n\t\tlong size = 0;\n\t\tByteBuffer[] bufs = new ByteBuffer[msgs.size()];\n\t\tfor (int i = 0; i < bufs.length; i++) {\n\t\t\tbyte[] b = toBytes(msgs.get(i));\n\t\t\tbufs[i] = ByteBuffer.wrap(b);\n\t\t\tsize += b.length;\n\t\t}\n\n\t\tdo {\n\t\t\tsize -= this.channel.write(bufs);\n\t\t} while (size != 0);\n\t\tthis.dirty = true;\n\t}\n\n\tprotected void sync() {\n\t\ttry {\n\t\t\tthis.channel.force(true);\n\t\t\tthis.dirty = false;\n\t\t\tConsoleLog.defaultLog.trace(\"{} finish sync to {}\", this.name, this.currentDate);\n\t\t} catch (Exception e) {\n\t\t\tConsoleLog.defaultLog.error(this.name + \"刷新到磁盘失败[\" + this.currentDate + \"]\", e);\n\t\t}\n\t}\n\n\tprotected void closeCurrentChannel() {\n\t\ttry {\n\t\t\tthis.channel.close();\n\t\t\tthis.channel = null;\n\t\t\tthis.currentDate = null;\n\t\t\tConsoleLog.defaultLog.debug(\"{} closed {}\", this.name, this.currentDate);\n\t\t} catch (Exception e) {\n\t\t\tConsoleLog.defaultLog.error(this.name + \"关闭失败[\" + this.currentDate + \"]\", e);\n\t\t}\n\t}\n\n\tprotected abstract boolean shouldDelete(String fileName);\n\n\tprotected byte[] toBytes(LogObject logObject) {\n\t\tif (this.buffer == null) {\n\t\t\tthis.buffer = new StringBuilder(bufferSize);\n\t\t} else {\n\t\t\tthis.buffer.setLength(0);\n\t\t}\n\t\tLogHelper.plainMessage(this.buffer, logObject, LogSettings.showAttach());\n\t\treturn this.buffer.toString().getBytes(LogObject.CHARSET);\n\t}\n\n\tprotected abstract String formatDateString(SumkDate date);\n\n\tprotected String extractPath(Map<String, String> map) {\n\t\tString path = map.get(LogAppenders.PATH);\n\t\tif (path != null || map.size() != 1) {\n\t\t\treturn path;\n\t\t}\n\n\t\tString p = map.keySet().iterator().next();\n\t\tString v = map.get(p);\n\t\treturn StringUtil.isEmpty(v) ? p : String.join(\":\", p, v);\n\t}\n\n\t@Override\n\tprotected boolean onStart(Map<String, String> map) {\n\t\tthis.config(map);\n\t\treturn this.dir != null && this.filePattern != null;\n\t}\n\n\t@Override\n\tpublic void config(Map<String, String> map) {\n\t\tString path = extractPath(map);\n\t\tif (!setupFilePath(path)) {\n\t\t\treturn;\n\t\t}\n\t\tsuper.config(map);\n\t}\n\n\tpublic File getLogFile(SumkDate date) {\n\t\tString name = filePattern.replace(SLOT, formatDateString(date));\n\t\treturn new File(dir, name);\n\t}\n\n\tpublic void setSyncInterval(int syncInterval) {\n\t\tthis.syncInterval = syncInterval;\n\t}\n\n\tpublic int getBufferSize() {\n\t\treturn bufferSize;\n\t}\n\n\tpublic void setBufferSize(int bufferSize) {\n\t\tthis.bufferSize = Math.max(bufferSize, 100);\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/SumkLoggerFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport org.slf4j.ILoggerFactory;\nimport org.slf4j.Logger;\nimport org.yx.log.LogLevel;\nimport org.yx.log.Loggers;\nimport org.yx.log.RawLog;\nimport org.yx.log.SumkLogger;\n\npublic final class SumkLoggerFactory implements ILoggerFactory {\n\n\tprivate static final Loggers loggers = Loggers.create(\"Slf4jLog\");\n\n\tprivate static Function<String, SumkLogger> loggerFactory = SumkLoggerImpl::new;\n\tstatic {\n\t\tLogAppenders.init();\n\t\tRawLog.setLogger(RawLog.SLF4J_LOG);\n\t}\n\n\tpublic SumkLoggerFactory() {\n\t\tRawLog.info(\"org.slf4j.sumk\", \"sumk slf4j initialized\");\n\t}\n\n\tpublic static Function<String, SumkLogger> getLoggerFactory() {\n\t\treturn loggerFactory;\n\t}\n\n\tpublic static void setLoggerFactory(Function<String, SumkLogger> loggerFactory) {\n\t\tSumkLoggerFactory.loggerFactory = Objects.requireNonNull(loggerFactory);\n\t}\n\n\tpublic static void setDefaultLevel(LogLevel level) {\n\t\tif (level != null) {\n\t\t\tLoggers.setDefaultLevel(level);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Logger getLogger(String name) {\n\t\tSumkLogger log = loggers.get(name);\n\t\tif (log != null) {\n\t\t\treturn log;\n\t\t}\n\t\tlog = loggerFactory.apply(name);\n\t\tSumkLogger oldInstance = loggers.putIfAbsent(name, log);\n\t\treturn oldInstance == null ? log : oldInstance;\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/SumkLoggerImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport org.slf4j.Marker;\nimport org.slf4j.event.LoggingEvent;\nimport org.slf4j.spi.LocationAwareLogger;\nimport org.yx.base.context.LogContext;\nimport org.yx.log.CodeLineMarker;\nimport org.yx.log.ConsoleLog;\nimport org.yx.log.LogKits;\nimport org.yx.log.LogLevel;\nimport org.yx.log.LogSettings;\nimport org.yx.log.SumkLogger;\nimport org.yx.util.SumkDate;\n\npublic class SumkLoggerImpl extends SumkLogger implements LocationAwareLogger {\n\n\tpublic SumkLoggerImpl(String module) {\n\t\tsuper(module);\n\t}\n\n\t@Override\n\tprotected void output(Marker marker, LogLevel methodLevel, String format, Object... arguments) {\n\t\ttry {\n\t\t\tString msg = LogKits.buildMessage(format, arguments);\n\t\t\tLogObject logObject = LogObject.create(marker, methodLevel, msg, null, this);\n\n\t\t\tif (!LogAppenders.offer(logObject) || LogSettings.consoleEnable()) {\n\t\t\t\tSystem.out.print(LogHelper.plainMessage(logObject, LogSettings.showAttach()));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\t@Override\n\tprotected void output(Marker marker, LogLevel methodLevel, String msg, Throwable e) {\n\t\ttry {\n\t\t\tLogObject logObject = LogObject.create(marker, methodLevel, msg, e, this);\n\t\t\tif (!LogAppenders.offer(logObject) || LogSettings.consoleEnable()) {\n\t\t\t\tSystem.err.print(LogHelper.plainMessage(logObject, LogSettings.showAttach()));\n\t\t\t}\n\t\t} catch (Exception e2) {\n\t\t\te2.printStackTrace();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void log(Marker marker, String fqcn, int level, String message, Object[] argArray, Throwable t) {\n\t\ttry {\n\t\t\tLogLevel methodLevel = LogKits.fromSlf4jLocationAwareLoggerInt(level);\n\t\t\tif (!this.isLogable(methodLevel)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString msg = LogKits.buildMessage(message, argArray);\n\t\t\tif (fqcn != null && !fqcn.equals(name)) {\n\t\t\t\tmarker = new CodeLineMarker(fqcn);\n\t\t\t}\n\t\t\tLogObject logObject = LogObject.create(marker, methodLevel, msg, t, this);\n\n\t\t\tif (!LogAppenders.offer(logObject) || LogSettings.consoleEnable()) {\n\t\t\t\tSystem.out.print(LogHelper.plainMessage(logObject, LogSettings.showAttach()));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tConsoleLog.defaultLog.error(\"failed when append to log queue\", e);\n\t\t}\n\t}\n\n\tpublic void log(LoggingEvent event) {\n\t\tLogLevel methodLevel = LogKits.fromSlf4jLocationAwareLoggerInt(event.getLevel().toInt());\n\t\tif (!this.isLogable(methodLevel)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tString msg = \"*** \" + LogKits.buildMessage(event.getMessage(), event.getArgumentArray());\n\t\t\tLogObject logObject = new LogObject(name, SumkDate.of(event.getTimeStamp()), methodLevel, msg,\n\t\t\t\t\tevent.getThrowable(), event.getThreadName(), LogContext.empty(), null);\n\n\t\t\tif (!LogAppenders.offer(logObject) || LogSettings.consoleEnable()) {\n\t\t\t\tSystem.out.print(LogHelper.plainMessage(logObject, LogSettings.showAttach()));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/UnionLog.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.Map;\n\npublic interface UnionLog {\n\n\tboolean start(Map<String, String> configMap);\n\n\tboolean isStarted();\n\n\tvoid stop() throws Exception;\n\n\tboolean offer(LogObject logObject);\n\n\tboolean directOffer(LogObject logObject);\n\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/UnionLogDao.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.List;\n\npublic interface UnionLogDao {\n\n\t/**\n\t * 一段时间触发,调用间隔一般不超过interval，但有可能接近0。有无日志都会调用\n\t * \n\t * @param idle true表示本次任务没有日志\n\t */\n\tvoid flush(boolean idle);\n\n\t/**\n\t * 输出日志对象,有日志的时候都会调用\n\t * \n\t * @param logs 日志对象列表，不为null，也不为空。里面的元素也都不为null\n\t * @throws Exception 如果抛出异常，会导致本次要处理的日志丢失，不影响之后日志的消费\n\t */\n\tvoid store(List<UnionLogObject> logs) throws Exception;\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/UnionLogObject.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.util.SumkDate;\n\n/**\n * 调用toLog()方法，可以获取到json化后的日志体。<BR>\n * 如果要减少中间态String的生成数量，可以调用getLogContext()获取CharSequence类型的日志体。\n */\npublic class UnionLogObject {\n\t@NotNull\n\tprotected final String name;\n\n\t@NotNull\n\tprotected final SumkDate date;\n\n\t@NotNull\n\tprotected final String log;\n\n\tpublic UnionLogObject(@NotNull String name, @NotNull SumkDate date, @NotNull String log) {\n\t\tthis.name = name;\n\t\tthis.date = date;\n\t\tthis.log = log;\n\t}\n\n\t/**\n\t * @return 日志名称\n\t */\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t/**\n\t * @return 日志打印时的时间\n\t */\n\tpublic SumkDate getDate() {\n\t\treturn date;\n\t}\n\n\t/**\n\t * 一般是json结构，并且里面字段的顺序一般也是固定的。 默认第一个前2个是name和date\n\t * \n\t * @return 格式化后的日志\n\t */\n\tpublic String getLog() {\n\t\treturn this.log;\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/UnionLogObjectSerializer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.io.IOException;\nimport java.util.function.Function;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.log.ConsoleLog;\n\npublic class UnionLogObjectSerializer implements Function<LogObject, UnionLogObject> {\n\n\tprivate final String appId;\n\n\tprivate int extraSize = 350;\n\n\tpublic UnionLogObjectSerializer() {\n\t\tthis.appId = AppInfo.appId(null);\n\t}\n\n\t@Override\n\tpublic UnionLogObject apply(LogObject log) {\n\t\tint estimate = extraSize;\n\t\tif (log.body != null) {\n\t\t\testimate += log.body.length();\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(estimate);\n\t\ttry {\n\t\t\tUnionLogUtil.appendLogObject(sb, log, appId);\n\t\t\treturn new UnionLogObject(log.loggerName, log.logDate, sb.toString());\n\t\t} catch (IOException e) {\n\t\t\tConsoleLog.defaultLog.error(\"数据解析出错\", e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic int getExtraSize() {\n\t\treturn extraSize;\n\t}\n\n\tpublic void setExtraSize(int extraSize) {\n\t\tif (extraSize > 0) {\n\t\t\tthis.extraSize = extraSize;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/UnionLogUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.yx.base.sumk.UnsafeStringWriter;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.CodeException;\nimport org.yx.log.ConsoleLog;\nimport org.yx.util.ExceptionUtil;\n\nimport com.google.gson.stream.JsonWriter;\n\npublic final class UnionLogUtil {\n\n\tprivate static File logRoot;\n\n\tprivate static String LOGING = System.getProperty(\"sumk.log.union.login\", \"loging\");\n\tprivate static String LOGED = System.getProperty(\"sumk.log.union.loged\", \"loged\");\n\tprivate static String ERROR = System.getProperty(\"sumk.log.union.error\", \"error\");\n\n\tpublic static void move2Loged(File logging) {\n\t\tif (logging == null) {\n\t\t\treturn;\n\t\t}\n\t\tFile dest = new File(getLogedPath(), logging.getName());\n\t\tFile p = dest.getParentFile();\n\t\tif (!p.exists()) {\n\t\t\tp.mkdirs();\n\t\t}\n\t\tif (!logging.renameTo(dest)) {\n\t\t\tConsoleLog.defaultLog.error(logging.getName() + \" move to loged folder failed\");\n\t\t}\n\t}\n\n\tpublic static void move2Error(File loged) {\n\t\tif (loged == null) {\n\t\t\treturn;\n\t\t}\n\t\tFile dest = new File(getErrorPath(), loged.getName());\n\t\tFile p = dest.getParentFile();\n\t\tif (!p.exists()) {\n\t\t\tp.mkdirs();\n\t\t}\n\t\tif (!loged.renameTo(dest)) {\n\t\t\tConsoleLog.defaultLog.error(loged.getName() + \" move to error path failed\");\n\t\t}\n\t}\n\n\tpublic static File getErrorPath() {\n\t\treturn new File(getLogRoot(), ERROR);\n\t}\n\n\tpublic static File getLogedPath() {\n\t\treturn new File(getLogRoot(), LOGED);\n\t}\n\n\tpublic static File getLogingPath() {\n\t\treturn new File(getLogRoot(), LOGING);\n\t}\n\n\tprivate static File getLogRoot() {\n\t\tif (logRoot != null) {\n\t\t\treturn logRoot;\n\t\t}\n\t\tlogRoot = getDefaultLoginPath();\n\t\tConsoleLog.defaultLog.info(\"logRoot:\" + logRoot.getAbsolutePath());\n\t\treturn logRoot;\n\t}\n\n\tprivate static File getDefaultLoginPath() {\n\t\tString path = System.getProperty(\"sumk.log.union.path\");\n\t\tif (path != null && path.length() > 2) {\n\t\t\treturn new File(path);\n\t\t}\n\t\tif (System.getProperty(\"os.name\").toLowerCase().startsWith(\"win\")) {\n\t\t\tFile f = new File(\"D:\");\n\t\t\tif (f.exists() && f.getFreeSpace() > 2000) {\n\t\t\t\treturn new File(f, \"log\\\\sumk\");\n\t\t\t}\n\t\t\treturn new File(\"C:\\\\log\\\\sumk\");\n\t\t}\n\t\treturn new File(\"/log/sumk\");\n\t}\n\n\tpublic static void appendLogObject(StringBuilder sb, LogObject log, String appId) throws IOException {\n\t\tUnsafeStringWriter stringWriter = new UnsafeStringWriter(sb);\n\t\tJsonWriter writer = new JsonWriter(stringWriter);\n\t\twriter.setSerializeNulls(false);\n\t\twriter.beginObject();\n\t\twriter.name(\"name\").value(log.loggerName);\n\t\twriter.name(\"date\").value(log.logDate.to_yyyy_MM_dd_HH_mm_ss_SSS());\n\t\twriter.name(\"userId\").value(log.userId());\n\t\twriter.name(\"traceId\").value(log.traceId());\n\t\twriter.name(\"spanId\").value(log.spanId());\n\t\tif (log.isTest()) {\n\t\t\twriter.name(\"test\").value(1);\n\t\t}\n\t\tString body = log.body;\n\t\twriter.name(\"body\").value(body);\n\t\twriter.name(\"threadName\").value(log.threadName);\n\t\twriter.name(\"level\").value(log.methodLevel.name());\n\t\twriter.name(\"host\").value(AppInfo.getLocalIp());\n\t\tif (appId != null) {\n\t\t\twriter.name(\"appId\").value(appId);\n\t\t}\n\t\twriter.name(\"pid\").value(AppInfo.pid());\n\t\tif (log.exception != null) {\n\t\t\twriter.name(\"exception\").value(log.exception.getClass().getName());\n\t\t\tStringBuilder sb2 = new StringBuilder(100);\n\t\t\tExceptionUtil.printStackTrace(sb2, log.exception);\n\t\t\twriter.name(\"exceptiondetail\").value(sb2.toString());\n\t\t\tif (log.exception instanceof CodeException) {\n\t\t\t\twriter.name(\"exceptioncode\").value(((CodeException) log.exception).getCode());\n\t\t\t}\n\t\t}\n\t\tif (log.codeLine != null) {\n\t\t\twriter.name(\"className\").value(log.codeLine.className);\n\t\t\twriter.name(\"methodName\").value(log.codeLine.methodName);\n\t\t\twriter.name(\"lineNumber\").value(log.codeLine.lineNumber);\n\t\t}\n\t\tMap<String, String> attachs = log.attachments();\n\t\tif (attachs != null) {\n\t\t\tfor (Entry<String, String> en : attachs.entrySet()) {\n\t\t\t\twriter.name(\"u_\" + en.getKey()).value(en.getValue());\n\t\t\t}\n\t\t}\n\t\twriter.endObject();\n\t\twriter.flush();\n\t\twriter.close();\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/main/java/org/yx/log/impl/UnionLogs.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log.impl;\n\nimport java.util.Collections;\nimport java.util.Objects;\n\npublic final class UnionLogs {\n\tprivate static UnionLog unionLog = new DefaultUnionLog();\n\n\tpublic static UnionLog getUnionLog() {\n\t\treturn unionLog;\n\t}\n\n\tpublic static void setUnionLog(UnionLog unionLog) {\n\t\tUnionLogs.unionLog = Objects.requireNonNull(unionLog);\n\t}\n\n\tpublic static boolean start() {\n\t\treturn unionLog.start(Collections.emptyMap());\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider",
    "content": "org.slf4j.v2.SumkServiceProvider"
  },
  {
    "path": "async-logger/src/test/java/org/test/ProxyLog.java",
    "content": "package org.test;\n\nimport org.slf4j.spi.LocationAwareLogger;\nimport org.yx.log.Log;\n\npublic class ProxyLog {\n\n\tprivate static LocationAwareLogger log=(LocationAwareLogger)Log.get(\"proxy-log\");\n\tprivate static final String FQCN=ProxyLog.class.getName();\n\tpublic static void log(String msg,Object... params) {\n\t\tlog.log(null, FQCN, LocationAwareLogger.INFO_INT, msg, params, null);\n\t}\n\t\n\tpublic static void error(String msg,Throwable t) {\n\t\tlog.log(null, FQCN, LocationAwareLogger.ERROR_INT, msg, null, t);\n\t}\n}\n"
  },
  {
    "path": "async-logger/src/test/java/org/test/Starter.java",
    "content": "package org.test;\n\nimport org.junit.Test;\nimport org.slf4j.spi.LocationAwareLogger;\nimport org.yx.log.Log;\nimport org.yx.util.SumkThreadPool;\n\npublic class Starter {\n\n\t@Test\n\tpublic void test() {\n\t\tLog.get(this.getClass()).info(\"{} 只是个测试类\",this.getClass().getSimpleName());\n\t\torg.apache.log4j.Logger.getLogger(this.getClass()).info(\"这是log4j的测试类\");\n\t\torg.apache.logging.log4j.LogManager.getLogger(this.getClass()).warn(\"这是log4j 2.x的测试类\");\n\t\torg.apache.logging.log4j.LogManager.getLogger(this.getClass()).error(\"如果使用log4j 2，请引入log4j-to-slf4j\");\n\t\tProxyLog.log(\"{}是{}的测试类\",\"这个\",LocationAwareLogger.class);\n\t\tProxyLog.error(\"异常消息\",new Exception(\"用于测试的异常\"));\n\t\t\n\t\tLog.get(this.getClass()).atWarn().addArgument(\"李四\").addKeyValue(\"key1\", \"marker1\")\n\t\t\t.setMessage(\"name:{}\").log();\n\t\tSumkThreadPool.shutdown();\n\t}\n\t\n}\n"
  },
  {
    "path": "async-logger/src/test/java/org/test/UnionDemo.java",
    "content": "package org.test;\n\nimport java.util.List;\n\nimport org.junit.Test;\nimport org.yx.log.Log;\nimport org.yx.log.impl.DefaultUnionLog;\nimport org.yx.log.impl.UnionLogDao;\nimport org.yx.log.impl.UnionLogObject;\nimport org.yx.log.impl.UnionLogs;\n\npublic class UnionDemo {\n\n\t@Test\n\tpublic void test() throws InterruptedException {\n\t\tDefaultUnionLog unionLog=(DefaultUnionLog) UnionLogs.getUnionLog();\n\t\tunionLog.setDao(new UnionLogDao(){\n\n\t\t\t@Override\n\t\t\tpublic void flush(boolean idle) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void store(List<UnionLogObject> logs) throws Exception {\n\t\t\t\tfor(UnionLogObject log:logs){\n\t\t\t\t\tSystem.out.println(log.getLog());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tUnionLogs.start();//启动统一日志\n\t\t\n//\t\tapp.propertis中配置了unionTest写入到统一日志\n\t\tLog.get(\"unionTest\").info(\"abc\");\n\t\tLog.get(\"unionTest\").info(\"222\");\n\t\tThread.sleep(1000);\n\t}\n\n}\n"
  },
  {
    "path": "async-logger/src/test/resources/app.properties",
    "content": "\nsumk.ioc=org.yx\n\n#用,或;做分隔符\nsumk.log.level=info,org.apache.zookeeper:warn\nsumk.log.console=1\n\n#kv之间用=做分隔符，kv对之间用；做分隔符\ns.log.day=path：D:\\log\\day-#.log; module:*\n\n#统一日志的配置\nsumk.unionlog.module=unionTest\n"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<groupId>com.github.youtongluan</groupId>\n\t<artifactId>sumk</artifactId>\n\t<version>4.2.1</version>\n\t<packaging>pom</packaging>\n\n\t<name>com.github.youtongluan:sumk</name>\n\t<description>A quick developing framewort for internet company</description>\n\t<url>https://github.com/youtongluan/sumk</url>\n\t<licenses>\n\t\t<license>\n\t\t\t<name>The Apache License, Version 2.0</name>\n\t\t\t<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n\t\t</license>\n\t</licenses>\n\t<scm>\n\t\t<url>https://github.com/youtongluan/sumk</url>\n\t\t<connection>https://github.com/youtongluan/sumk.git</connection>\n\t\t<developerConnection>https://github.com/youtongluan/sumk</developerConnection>\n\t</scm>\n\t<distributionManagement>\n\t    <repository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n\t    </repository>\n\t    <snapshotRepository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n\t    </snapshotRepository>\n\t</distributionManagement>\n\t<developers>\n\t\t<developer>\n\t\t\t<name>youtongluan</name>\n\t\t\t<email>3205207767@qq.com</email>\n\t\t\t<url>https://www.oschina.net/p/sumk</url>\n\t\t</developer>\n\t</developers>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<maven.test.skip>true</maven.test.skip>\n\t\t<slf4j-version>2.0.17</slf4j-version>\n\t</properties>\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t\t<artifactId>commons-dbcp2</artifactId>\n\t\t\t\t<version>2.9.0</version>\n\t\t\t\t<exclusions>\n\t\t\t\t\t<exclusion>\n\t\t\t\t\t\t<artifactId>commons-logging</artifactId>\n\t\t\t\t\t\t<groupId>commons-logging</groupId>\n\t\t\t\t\t</exclusion>\n\t\t\t\t</exclusions>\n\t\t\t</dependency>\n\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.google.code.gson</groupId>\n\t\t\t\t<artifactId>gson</artifactId>\n\t\t\t\t<version>2.10.1</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>mysql</groupId>\n\t\t\t\t<artifactId>mysql-connector-java</artifactId>\n\t\t\t\t<version>8.0.33</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.ow2.asm</groupId>\n\t\t\t\t<artifactId>asm</artifactId>\n\t\t\t\t<version>9.2</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>redis.clients</groupId>\n\t\t\t\t<artifactId>jedis</artifactId>\n\t\t\t\t<version>3.7.0</version>\n\t\t\t</dependency>\n\n\t\t\t<dependency>\n\t\t  <groupId>org.slf4j</groupId>\n\t\t  <artifactId>slf4j-api</artifactId>\n\t\t  <version>${slf4j-version}</version>\n\t\t</dependency>\n\t\t<!-- log4j 输出到 slf4j，日志级别设置也由slf4j的实现类决定 -->\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>log4j-over-slf4j</artifactId>\n\t\t\t<version>${slf4j-version}</version>\n\t\t</dependency>\n\t\t<!-- commons-logging 输出到 slf4j -->\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>jcl-over-slf4j</artifactId>\n\t\t\t<version>${slf4j-version}</version>\n\t\t</dependency>\n\n\t\t\t<!-- 如果不用mybatis，这个可以去掉 -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.mybatis</groupId>\n\t\t\t\t<artifactId>mybatis</artifactId>\n\t\t\t\t<version>3.5.0</version>\n\t\t\t</dependency>\n\n\n\t\t\t<!-- rpc中使用，如果不用rpc，可以去掉 -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.zookeeper</groupId>\n\t\t\t\t<artifactId>zookeeper</artifactId>\n\t\t\t\t<version>3.9.3</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>io.netty</groupId>\n\t\t\t\t<artifactId>netty-handler</artifactId>\n\t\t\t\t<version>4.1.107.Final</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.101tec</groupId>\n\t\t\t\t<artifactId>zkclient</artifactId>\n\t\t\t\t<version>0.11</version>\n\t\t\t</dependency>\n\t\t\t<!-- rpc中使用结束 -->\n\n\t\t\t<!-- http中使用，如果不用jetty，可以去掉 -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t\t<artifactId>jetty-servlet</artifactId>\n\t\t\t\t<version>9.4.51.v20230217</version>\n\t\t\t</dependency>\n\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.httpcomponents</groupId>\n\t\t\t\t<artifactId>httpasyncclient</artifactId>\n\t\t\t\t<version>4.0.2</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.httpcomponents</groupId>\n\t\t\t\t<artifactId>httpmime</artifactId>\n\t\t\t\t<version>4.2</version>\n\t\t\t</dependency>\n\t\t\t<!-- http中使用结束 -->\n\n\t\t\t<dependency>\n\t\t\t\t<groupId>junit</groupId>\n\t\t\t\t<artifactId>junit</artifactId>\n\t\t\t\t<version>4.13.2</version>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\t<modules>\n\t\t<module>sumk-base</module>\n\t\t<module>sumk-framework</module>\n\t\t<module>sumk-db</module>\n\t\t<module>sumk-http</module>\n\t\t<module>sumk-rpc</module>\n\t\t<module>sumk-rpc-mina</module>\n\t\t<module>async-logger</module>\n\t</modules>\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t<inherited>true</inherited>\n\t\t\t\t<configuration>\n\t\t\t\t\t<source>1.8</source>\n\t\t\t\t\t<target>1.8</target>\n\t\t\t\t\t<encoding>UTF-8</encoding>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-resources-plugin</artifactId>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>copy-license</id>\n\t\t\t\t\t\t<phase>process-sources</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>copy-resources</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<outputDirectory>${basedir}/target/classes/META-INF</outputDirectory>\n\t\t\t\t\t\t\t<resources>\n\t\t\t\t\t\t\t\t<resource>\n\t\t\t\t\t\t\t\t\t<directory>${basedir}</directory>\n\t\t\t\t\t\t\t\t\t<includes>\n\t\t\t\t\t\t\t\t\t\t<include>LICENSE</include>\n\t\t\t\t\t\t\t\t\t</includes>\n\t\t\t\t\t\t\t\t</resource>\n\t\t\t\t\t\t\t</resources>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<skipTests>true</skipTests>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-gpg-plugin</artifactId>\n\t\t\t\t<version>1.6</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>sign-artifacts</id>\n\t\t\t\t\t\t<phase>verify</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>sign</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t\t<configuration>\n\t\t\t\t\t<skip>false</skip>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t          <groupId>org.sonatype.central</groupId>\n\t          <artifactId>central-publishing-maven-plugin</artifactId>\n\t          <version>0.8.0</version>\n\t          <extensions>true</extensions>\n\t          <configuration>\n\t            <publishingServerId>central</publishingServerId>\n\t          </configuration>\n        \t</plugin>\n\t\t</plugins>\n\t</build>\n</project>"
  },
  {
    "path": "sql.dtd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n\r\n<!ELEMENT sdb (sql+)>\r\n<!ATTLIST sdb\r\nnamespace CDATA #IMPLIED\r\n>\r\n\r\n<!ELEMENT sql (#PCDATA|if|ifnot|items|foreach)*>\r\n<!ATTLIST sql\r\nid ID #REQUIRED\r\n>\r\n\r\n<!ELEMENT if (#PCDATA|if|ifnot|items|foreach)*>\r\n<!ATTLIST if\r\ntest CDATA #REQUIRED\r\nfalseby (null|nokey|empty) #IMPLIED\r\n>\r\n\r\n<!ELEMENT ifnot (#PCDATA|if|ifnot|items|foreach)*>\r\n<!ATTLIST ifnot\r\ntest CDATA #REQUIRED\r\nfalseby (null|nokey|empty) #IMPLIED\r\n>\r\n\r\n<!ELEMENT items (if|ifnot|items|foreach)+>\r\n<!ATTLIST items\r\nseparator CDATA #REQUIRED\r\nopen CDATA #IMPLIED\r\nclose CDATA #IMPLIED\r\n>\r\n\r\n<!ELEMENT foreach (#PCDATA)>\r\n<!ATTLIST foreach\r\ncollection CDATA #REQUIRED\r\nseparator CDATA #REQUIRED\r\nitem CDATA #IMPLIED\r\nopen CDATA #IMPLIED\r\nclose CDATA #IMPLIED\r\n>\r\n"
  },
  {
    "path": "sumk-base/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright youtongluan (\\u6e38\\u901a\\u92ae\\uff0c\\u522b\\u540d\\uff1a\\u6e38\\u590f)\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "sumk-base/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.github.youtongluan</groupId>\n\t\t<artifactId>sumk</artifactId>\n\t\t<version>4.2.1</version>\n\t</parent>\n\t<artifactId>sumk-base</artifactId>\n\t<name>com.github.youtongluan:sumk</name>\n\t<description>A quick developing framewort for internet company</description>\n\t<url>https://github.com/youtongluan/sumk</url>\n\t<licenses>\n\t\t<license>\n\t\t\t<name>The Apache License, Version 2.0</name>\n\t\t\t<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n\t\t</license>\n\t</licenses>\n\t<scm>\n\t\t<url>https://github.com/youtongluan/sumk</url>\n\t\t<connection>https://github.com/youtongluan/sumk.git</connection>\n\t\t<developerConnection>https://github.com/youtongluan/sumk</developerConnection>\n\t</scm>\n\t<distributionManagement>\n\t    <repository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n\t    </repository>\n\t    <snapshotRepository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n\t    </snapshotRepository>\n\t</distributionManagement>\n\t<developers>\n\t\t<developer>\n\t\t\t<name>youtongluan</name>\n\t\t\t<email>3205207767@qq.com</email>\n\t\t\t<url>https://www.oschina.net/p/sumk</url>\n\t\t</developer>\n\t</developers>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.google.code.gson</groupId>\n\t\t\t<artifactId>gson</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t  <groupId>org.slf4j</groupId>\n\t\t  <artifactId>slf4j-api</artifactId>\n\t\t</dependency>\n\t\t<!-- log4j 输出到 slf4j，日志级别设置也由slf4j的实现类决定 -->\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>log4j-over-slf4j</artifactId>\n\t\t</dependency>\n\t\t<!-- commons-logging 输出到 slf4j -->\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>jcl-over-slf4j</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/annotation/doc/Comment.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.doc;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 备注信息。目前可用于@Web、@Soa、@Table、@Column\n *\n */\n@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.FIELD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Comment {\n\n\tString value();\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/annotation/doc/NotNull.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.doc;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 本注解是一种规范，约定该字段或者参数不为null。<BR>\n * 比如某个参数不能为null，而内部的方法，可能为了性能考虑，并没有做非空判断\n *\n */\n@Target({ ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD, ElementType.LOCAL_VARIABLE })\n@Retention(RetentionPolicy.SOURCE)\n@Documented\npublic @interface NotNull {\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/Executable.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base;\n\n/**\n * Runnable的替代品\n */\npublic interface Executable {\n\tvoid run() throws Throwable;\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/ItemJoiner.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base;\n\npublic final class ItemJoiner {\n\n\tpublic static ItemJoiner create(CharSequence delimiter, CharSequence pre, CharSequence suf) {\n\t\treturn new ItemJoiner(delimiter, pre, suf);\n\t}\n\n\tprivate StringBuilder sb = new StringBuilder();\n\tprivate final CharSequence delimiter;\n\tprivate final CharSequence prefix;\n\tprivate final CharSequence suffix;\n\tprivate boolean hasDelimiter;\n\n\tpublic ItemJoiner(CharSequence delimiter, CharSequence pre, CharSequence suf) {\n\t\tthis.delimiter = delimiter;\n\t\tthis.prefix = pre;\n\t\tthis.suffix = suf;\n\t}\n\n\tpublic ItemJoiner(CharSequence delimiter) {\n\t\tthis(delimiter, null, null);\n\t}\n\n\tpublic ItemJoiner item() {\n\t\tif (sb.length() > 0) {\n\t\t\tsb.append(this.delimiter);\n\t\t\thasDelimiter = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic ItemJoiner append(CharSequence v) {\n\t\tsb.append(v);\n\t\treturn this;\n\t}\n\n\tpublic ItemJoiner append(char v) {\n\t\tsb.append(v);\n\t\treturn this;\n\t}\n\n\tpublic ItemJoiner append(Object v) {\n\t\tsb.append(v);\n\t\treturn this;\n\t}\n\n\tpublic ItemJoiner append(ItemJoiner item, CharSequence pre, CharSequence sub) {\n\t\tif (item == null) {\n\t\t\treturn this;\n\t\t}\n\t\tif (item.hasDelimiter && pre != null) {\n\t\t\tsb.append(pre);\n\t\t}\n\t\tsb.append(item.toCharSequence());\n\t\tif (item.hasDelimiter && sub != null) {\n\t\t\tsb.append(sub);\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic CharSequence toCharSequence() {\n\t\treturn this.toCharSequence(false);\n\t}\n\n\tpublic CharSequence toCharSequence(boolean forcePreAndSubFix) {\n\t\tif (sb == null || sb.length() == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!forcePreAndSubFix && !hasDelimiter) {\n\t\t\treturn sb;\n\t\t}\n\t\tint len = sb.length();\n\t\tif (this.prefix != null) {\n\t\t\tlen += this.prefix.length();\n\t\t}\n\t\tif (this.suffix != null) {\n\t\t\tlen += this.suffix.length();\n\t\t}\n\t\tStringBuilder ret = new StringBuilder(len);\n\t\tif (this.prefix != null) {\n\t\t\tret.append(this.prefix);\n\t\t}\n\t\tret.append(sb);\n\t\tif (this.suffix != null) {\n\t\t\tret.append(this.suffix);\n\t\t}\n\t\treturn ret;\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn sb == null || sb.length() == 0;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn String.valueOf(this.toCharSequence());\n\t}\n\n\tpublic ItemJoiner appendNotEmptyItem(CharSequence item) {\n\t\tif (item == null || item.length() == 0) {\n\t\t\treturn this;\n\t\t}\n\t\tthis.item().append(item);\n\t\treturn this;\n\t}\n\n\tpublic void append(long v) {\n\t\tthis.append(String.valueOf(v));\n\t}\n\n\tpublic void append(int v) {\n\t\tthis.append(String.valueOf(v));\n\t}\n\n\tpublic ItemJoiner copy() {\n\t\treturn new ItemJoiner(delimiter, prefix, suffix);\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/Lifecycle.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base;\n\npublic interface Lifecycle {\n\n\t/**\n\t * 如果抛出异常，会导致启动失败\n\t */\n\tvoid start();\n\n\tvoid stop();\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/Ordered.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base;\n\npublic interface Ordered extends Comparable<Ordered> {\n\n\tpublic static int DEFAULT_ORDER = 100;\n\n\t/**\n\t * 升序，值越大，优先级越低。一般不采用负数\n\t * \n\t * @return 索引值\n\t */\n\tdefault int order() {\n\t\treturn DEFAULT_ORDER;\n\t}\n\n\t@Override\n\tdefault int compareTo(Ordered o) {\n\t\treturn Integer.compare(this.order(), o.order());\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/StartOnceLifecycle.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base;\n\npublic abstract class StartOnceLifecycle implements Lifecycle {\n\n\tprotected boolean started;\n\n\t@Override\n\tpublic final synchronized void start() {\n\t\tif (started) {\n\t\t\treturn;\n\t\t}\n\t\tstarted = true;\n\t\tthis.onStart();\n\t}\n\n\tprotected abstract void onStart();\n\n\t@Override\n\tpublic void stop() {\n\t\tthis.started = false;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/context/ActionContext.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.context;\n\nimport java.util.Map;\n\nimport org.yx.base.Executable;\nimport org.yx.base.sumk.map.ListMap;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\n\npublic final class ActionContext implements Cloneable {\n\n\tprivate LogContext logContext;\n\n\t/**\n\t * 用来做自增长的\n\t */\n\tprivate int spanSeed;\n\n\tpublic boolean isTest() {\n\t\treturn logContext.test;\n\t}\n\n\tActionContext(LogContext logContext) {\n\t\tthis.logContext = logContext;\n\t}\n\n\tpublic String act() {\n\t\treturn logContext.act;\n\t}\n\n\tpublic String traceId() {\n\t\treturn logContext.traceId;\n\t}\n\n\tpublic String spanId() {\n\t\treturn logContext.spanId;\n\t}\n\n\tprivate static final ThreadLocal<ActionContext> holder = new ThreadLocal<ActionContext>() {\n\n\t\t@Override\n\t\tprotected ActionContext initialValue() {\n\n\t\t\treturn new ActionContext(LogContext.EMPTY);\n\t\t}\n\n\t};\n\n\tpublic static ActionContext newContext(String act, String traceId, boolean test) {\n\t\treturn newContext(act, traceId, null, null, test, null);\n\t}\n\n\tpublic static ActionContext newContext(String act, String traceId, String spanId, String userId, boolean isTest,\n\t\t\tMap<String, String> attachments) {\n\t\tLogContext logContext = LogContext.create(act, traceId, spanId, userId, isTest && AppContext.inst().isTest(),\n\t\t\t\tattachments);\n\t\treturn newContext(logContext);\n\t}\n\n\tpublic static ActionContext newContext(LogContext logContext) {\n\t\tActionContext c = new ActionContext(logContext);\n\t\tholder.set(c);\n\t\treturn c;\n\t}\n\n\tpublic static Runnable wrap(Runnable r) {\n\t\tActionContext ac = ActionContext.current();\n\t\treturn () -> {\n\t\t\tActionContext.store(ac);\n\t\t\ttry {\n\t\t\t\tr.run();\n\t\t\t} finally {\n\t\t\t\tActionContext.remove();\n\t\t\t}\n\t\t};\n\t}\n\n\tpublic static Runnable wrapExecutable(Executable r) {\n\t\tActionContext ac = ActionContext.current();\n\t\treturn () -> {\n\t\t\tActionContext.store(ac);\n\t\t\ttry {\n\t\t\t\tr.run();\n\t\t\t} catch (Throwable e) {\n\t\t\t\tLog.get(\"sumk.execute\").error(r + \"异步执行出错,\" + e.getLocalizedMessage(), e);\n\t\t\t} finally {\n\t\t\t\tActionContext.remove();\n\t\t\t}\n\t\t};\n\t}\n\n\tpublic static void store(ActionContext c) {\n\t\tholder.set(c);\n\t}\n\n\tpublic static ActionContext current() {\n\t\treturn holder.get();\n\t}\n\n\tpublic static void remove() {\n\t\tholder.remove();\n\t}\n\n\tpublic void setTraceIdIfAbsent(String traceId) {\n\t\tLogContext lc = this.logContext;\n\t\tif (lc.traceId != null) {\n\t\t\treturn;\n\t\t}\n\t\tthis.logContext = LogContext.create(lc.act, traceId, lc.spanId, lc.userId, lc.test, lc.attachments);\n\t}\n\n\tpublic String userId() {\n\t\treturn logContext.userId;\n\t}\n\n\tpublic void userId(String userId) {\n\t\tLogContext lc = this.logContext;\n\t\tthis.logContext = LogContext.create(lc.act, lc.traceId, lc.spanId, userId, lc.test, lc.attachments);\n\t}\n\n\tpublic Map<String, String> attachmentView() {\n\t\treturn logContext.attachments;\n\t}\n\n\t/**\n\t * 设置上下文的附加属性\n\t * \n\t * @param key   序列化后的长度要在255以内\n\t * @param value 序列化后的长度要在60K以内。如果value为null，就相当于remove\n\t */\n\tpublic void setAttachment(String key, String value) {\n\t\tif (value == null) {\n\t\t\tif (this.logContext.attachments != null) {\n\t\t\t\tMap<String, String> attachments = new ListMap<>(this.logContext.attachments);\n\t\t\t\tattachments.remove(key);\n\t\t\t\tthis.logContext = LogContext.create(this.logContext, attachments);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tMap<String, String> attachments = this.logContext.attachments;\n\t\tattachments = attachments == null ? new ListMap<>() : new ListMap<>(attachments, 1);\n\t\tattachments.put(key, value);\n\t\tthis.logContext = LogContext.create(this.logContext, attachments);\n\t}\n\n\tpublic String getAttachment(String key) {\n\t\tMap<String, String> attachments = this.logContext.attachments;\n\t\tif (attachments == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn attachments.get(key);\n\t}\n\n\tpublic String nextSpanId() {\n\t\tLogContext lc = this.logContext;\n\t\tif (lc.traceId == null) {\n\t\t\treturn \"1\";\n\t\t}\n\t\tint seed;\n\t\tsynchronized (this) {\n\t\t\tseed = ++this.spanSeed;\n\t\t}\n\t\tString sp = lc.spanId;\n\t\tif (sp == null) {\n\t\t\treturn String.valueOf(seed);\n\t\t}\n\t\treturn new StringBuilder().append(lc.spanId).append('.').append(seed).toString();\n\t}\n\n\tpublic LogContext logContext() {\n\t\treturn this.logContext;\n\t}\n\n\t@Override\n\tpublic ActionContext clone() {\n\t\ttry {\n\t\t\treturn (ActionContext) super.clone();\n\t\t} catch (CloneNotSupportedException e) {\n\t\t\tthrow new SumkException(8234235, \"clone not supported\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/context/AppContext.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.context;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.yx.conf.AppInfo;\n\n/**\n * 这个只在系统启动的时候才能使用\n */\npublic final class AppContext {\n\n\tprivate static AppContext inst = new AppContext();\n\tprivate volatile boolean started;\n\tprivate volatile boolean destoryed;\n\tprivate boolean test;\n\n\tpublic static AppContext inst() {\n\t\treturn inst;\n\t}\n\n\tprivate AppContext() {\n\n\t}\n\n\tprivate final ConcurrentMap<String, Object> map = new ConcurrentHashMap<>();\n\n\tpublic void put(String key, Object obj) {\n\t\tmap.put(key, obj);\n\t}\n\n\tpublic void put(Class<?> clz, Object obj) {\n\t\tmap.put(clz.getName(), obj);\n\t}\n\n\tpublic Object get(String key) {\n\t\treturn map.get(key);\n\t}\n\n\tpublic Object get(Class<?> clz) {\n\t\treturn map.get(clz.getName());\n\t}\n\n\tpublic <T> T get(Class<T> clz, T defaultObject) {\n\t\tString key = clz.getName();\n\t\tObject old = map.get(key);\n\t\treturn clz.isInstance(old) ? clz.cast(old) : defaultObject;\n\t}\n\n\tpublic static void clear() {\n\t\tinst = new AppContext();\n\t}\n\n\tpublic static void startFailed() {\n\t\tif (AppInfo.getBoolean(\"sumk.exitIfStartFail\", true)) {\n\t\t\tSystem.exit(1);\n\t\t}\n\t}\n\n\tpublic String getAppInfo(String key, String defaultV) {\n\t\tObject v = map.get(key);\n\n\t\tif (v instanceof String) {\n\t\t\treturn (String) v;\n\t\t}\n\t\treturn AppInfo.get(key, defaultV);\n\t}\n\n\tpublic boolean isStarted() {\n\t\treturn started;\n\t}\n\n\tpublic boolean isDestoryed() {\n\t\treturn destoryed;\n\t}\n\n\tpublic void resetStatus() {\n\t\tstarted = false;\n\t\tdestoryed = false;\n\t}\n\n\tpublic void setStatusToStarted() {\n\t\tstarted = true;\n\t\tdestoryed = false;\n\t}\n\n\tpublic void setStatusToDestoryed() {\n\t\tstarted = false;\n\t\tdestoryed = true;\n\t}\n\n\tpublic boolean isTest() {\n\t\treturn test;\n\t}\n\n\tpublic void setTest(boolean test) {\n\t\tthis.test = test;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/context/LogContext.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.context;\n\nimport java.util.Map;\n\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic final class LogContext {\n\tstatic final LogContext EMPTY = new LogContext(null, null, null, null, false, null);\n\n\tpublic final String act;\n\n\tpublic final String traceId;\n\n\tpublic final String spanId;\n\n\tpublic final String userId;\n\n\tpublic final boolean test;\n\n\tfinal Map<String, String> attachments;\n\n\tpublic static LogContext create(String act, String traceId, String spanId, String userId, boolean test,\n\t\t\tMap<String, String> attachments) {\n\t\tif (attachments != null && attachments.size() > 0) {\n\t\t\treturn new LogContext(act, traceId, spanId, userId, test, attachments);\n\t\t}\n\n\t\tif (act == null && traceId == null && spanId == null && userId == null && !test) {\n\t\t\treturn EMPTY;\n\t\t}\n\t\treturn new LogContext(act, traceId, spanId, userId, test, null);\n\t}\n\n\tpublic static LogContext create(LogContext lc, Map<String, String> attachments) {\n\t\treturn create(lc.act, lc.traceId, lc.spanId, lc.userId, lc.test, attachments);\n\t}\n\n\tprivate LogContext(String act, String traceId, String spanId, String userId, boolean test,\n\t\t\tMap<String, String> attachments) {\n\t\tthis.act = act;\n\t\tthis.traceId = StringUtil.isEmpty(traceId) ? null : traceId;\n\t\tthis.spanId = spanId;\n\t\tthis.userId = userId;\n\t\tthis.test = test;\n\t\tthis.attachments = attachments == null ? null : CollectionUtil.unmodifyMap(attachments);\n\t}\n\n\tpublic static LogContext empty() {\n\t\treturn EMPTY;\n\t}\n\n\tpublic Map<String, String> unmodifiedAttachs() {\n\t\treturn this.attachments;\n\t}\n\n\tpublic String getAct() {\n\t\treturn act;\n\t}\n\n\tpublic String getTraceId() {\n\t\treturn traceId;\n\t}\n\n\tpublic String getSpanId() {\n\t\treturn spanId;\n\t}\n\n\tpublic String getUserId() {\n\t\treturn userId;\n\t}\n\n\tpublic boolean isTest() {\n\t\treturn test;\n\t}\n}"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/date/DateAdapters.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.date;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\n\nimport org.yx.util.SumkDate;\n\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.TypeAdapter;\nimport com.google.gson.stream.JsonReader;\nimport com.google.gson.stream.JsonToken;\nimport com.google.gson.stream.JsonWriter;\n\npublic final class DateAdapters {\n\n\tpublic static void registerAll(GsonBuilder builder) {\n\t\tbuilder.registerTypeAdapter(LocalDateTime.class, localDateTimeAdapter);\n\t\tbuilder.registerTypeAdapter(LocalDate.class, localDateAdapter);\n\t\tbuilder.registerTypeAdapter(LocalTime.class, localTimeAdapter);\n\t\tbuilder.registerTypeAdapter(SumkDate.class, sumkDateAdapter);\n\t\tbuilder.registerTypeAdapter(java.sql.Date.class, sqlDateAdapter);\n\t}\n\n\tpublic static final TypeAdapter<LocalDateTime> localDateTimeAdapter = new TypeAdapter<LocalDateTime>() {\n\n\t\t@Override\n\t\tpublic void write(JsonWriter out, LocalDateTime value) throws IOException {\n\t\t\tif (value == null) {\n\t\t\t\tout.nullValue();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tout.value(SumkDate.of(value).to_yyyy_MM_dd_HH_mm_ss_SSS());\n\t\t}\n\n\t\t@Override\n\t\tpublic LocalDateTime read(JsonReader in) throws IOException {\n\t\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\t\tin.nextNull();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString v = in.nextString();\n\t\t\treturn SumkDate.of(v).toLocalDateTime();\n\t\t}\n\t};\n\n\tpublic static final TypeAdapter<LocalDate> localDateAdapter = new TypeAdapter<LocalDate>() {\n\n\t\t@Override\n\t\tpublic void write(JsonWriter out, LocalDate value) throws IOException {\n\t\t\tif (value == null) {\n\t\t\t\tout.nullValue();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tout.value(SumkDate.of(value, null).to_yyyy_MM_dd());\n\t\t}\n\n\t\t@Override\n\t\tpublic LocalDate read(JsonReader in) throws IOException {\n\t\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\t\tin.nextNull();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString v = in.nextString();\n\t\t\treturn SumkDate.of(v, SumkDate.yyyy_MM_dd).toLocalDate();\n\t\t}\n\t};\n\n\tpublic static final TypeAdapter<LocalTime> localTimeAdapter = new TypeAdapter<LocalTime>() {\n\n\t\t@Override\n\t\tpublic void write(JsonWriter out, LocalTime value) throws IOException {\n\t\t\tif (value == null) {\n\t\t\t\tout.nullValue();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tout.value(SumkDate.of(null, value).to_HH_mm_ss_SSS());\n\t\t}\n\n\t\t@Override\n\t\tpublic LocalTime read(JsonReader in) throws IOException {\n\t\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\t\tin.nextNull();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString v = in.nextString();\n\t\t\treturn SumkDate.of(v, SumkDate.HH_mm_ss_SSS).toLocalTime();\n\t\t}\n\t};\n\n\tpublic static final TypeAdapter<SumkDate> sumkDateAdapter = new TypeAdapter<SumkDate>() {\n\n\t\t@Override\n\t\tpublic void write(JsonWriter out, SumkDate sd) throws IOException {\n\t\t\tif (sd == null) {\n\t\t\t\tout.nullValue();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tout.value(sd.to_yyyy_MM_dd_HH_mm_ss_SSS());\n\t\t}\n\n\t\t@Override\n\t\tpublic SumkDate read(JsonReader in) throws IOException {\n\t\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\t\tin.nextNull();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString v = in.nextString();\n\t\t\ttry {\n\t\t\t\treturn SumkDate.of(v);\n\t\t\t} catch (Exception e) {\n\t\t\t\tString num = v;\n\t\t\t\tif (num.contains(\".\")) {\n\t\t\t\t\tnum = num.substring(0, num.indexOf('.'));\n\t\t\t\t}\n\t\t\t\treturn SumkDate.of(Long.parseLong(num));\n\t\t\t}\n\t\t}\n\t};\n\n\tpublic static final TypeAdapter<java.sql.Date> sqlDateAdapter = new TypeAdapter<java.sql.Date>() {\n\n\t\t@Override\n\t\tpublic void write(JsonWriter out, java.sql.Date d) throws IOException {\n\t\t\tif (d == null) {\n\t\t\t\tout.nullValue();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tout.value(SumkDate.of(d).to_yyyy_MM_dd());\n\t\t}\n\n\t\t@Override\n\t\tpublic java.sql.Date read(JsonReader in) throws IOException {\n\t\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\t\tin.nextNull();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString v = in.nextString();\n\t\t\treturn TimeUtil.toType(SumkDate.of(v, SumkDate.yyyy_MM_dd), java.sql.Date.class, true);\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/date/DateFormater.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.date;\n\nimport org.yx.util.SumkDate;\n\n/**\n * yyyy-MM-dd\n */\npublic final class DateFormater implements SumkDateFormater {\n\n\tpublic static final DateFormater inst = new DateFormater();\n\n\t@Override\n\tpublic int order() {\n\t\treturn 50;\n\t}\n\n\t@Override\n\tpublic SumkDate parse(String text) {\n\t\tint firstIndex = text.length() - 6;\n\t\tint year = Integer.parseInt(text.substring(0, firstIndex));\n\t\tint month = Integer.parseInt(text.substring(firstIndex + 1, firstIndex + 3));\n\t\tint day = Integer.parseInt(text.substring(firstIndex + 4));\n\t\treturn SumkDate.of(year, month, day, 0, 0, 0, 0);\n\t}\n\n\t@Override\n\tpublic boolean accept(String format) {\n\t\treturn format.length() == 10 && format.regionMatches(0, \"yyyy\", 0, 4) && format.regionMatches(5, \"MM\", 0, 2)\n\t\t\t\t&& format.endsWith(\"dd\");\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/date/DateTimeFormater.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.date;\n\nimport org.yx.util.SumkDate;\n\n/**\n * yyyy-MM-dd HH:mm:ss\n */\npublic final class DateTimeFormater implements SumkDateFormater {\n\n\tpublic static final DateTimeFormater inst = new DateTimeFormater();\n\n\tprivate DateTimeFormater() {\n\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn 20;\n\t}\n\n\t@Override\n\tpublic SumkDate parse(String text) {\n\t\tint firstIndex = text.length() - 15;\n\t\tint year = Integer.parseInt(text.substring(0, firstIndex));\n\t\tint month = Integer.parseInt(text.substring(firstIndex + 1, firstIndex + 3));\n\t\tint day = Integer.parseInt(text.substring(firstIndex + 4, firstIndex + 6));\n\t\tint hour = Integer.parseInt(text.substring(firstIndex + 7, firstIndex + 9));\n\t\tint minute = Integer.parseInt(text.substring(firstIndex + 10, firstIndex + 12));\n\t\tint second = Integer.parseInt(text.substring(firstIndex + 13));\n\t\treturn SumkDate.of(year, month, day, hour, minute, second, 0);\n\t}\n\n\t@Override\n\tpublic boolean accept(String format) {\n\t\treturn format.length() == 19 && format.regionMatches(0, \"yyyy\", 0, 4) && format.regionMatches(5, \"MM\", 0, 2)\n\t\t\t\t&& format.regionMatches(8, \"dd\", 0, 2) && format.regionMatches(11, \"HH\", 0, 2)\n\t\t\t\t&& format.regionMatches(14, \"mm\", 0, 2) && format.endsWith(\"ss\");\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/date/DateTimeTypeAdapter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.date;\n\nimport java.io.IOException;\nimport java.util.Date;\n\nimport org.yx.util.StringUtil;\nimport org.yx.util.SumkDate;\n\nimport com.google.gson.TypeAdapter;\nimport com.google.gson.stream.JsonReader;\nimport com.google.gson.stream.JsonToken;\nimport com.google.gson.stream.JsonWriter;\n\npublic final class DateTimeTypeAdapter extends TypeAdapter<Date> {\n\n\tprivate String dateFormat = SumkDate.yyyy_MM_dd_HH_mm_ss_SSS;\n\n\tpublic void setDateFormat(String format) {\n\t\tif (StringUtil.isEmpty(format)) {\n\t\t\tthis.dateFormat = null;\n\t\t}\n\t\tthis.dateFormat = format;\n\t}\n\n\t@Override\n\tpublic Date read(JsonReader in) throws IOException {\n\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\tin.nextNull();\n\t\t\treturn null;\n\t\t}\n\t\treturn deserializeToDate(in.nextString());\n\t}\n\n\tprivate Date deserializeToDate(final String text) throws IOException {\n\t\ttry {\n\t\t\treturn SumkDate.of(text).toDate();\n\t\t} catch (Exception e) {\n\t\t}\n\t\tString num = text;\n\t\tif (num.contains(\".\")) {\n\n\t\t\tnum = num.substring(0, num.indexOf('.'));\n\t\t}\n\t\treturn new Date(Long.parseLong(num));\n\t}\n\n\t@Override\n\tpublic void write(JsonWriter out, Date value) throws IOException {\n\t\tif (value == null) {\n\t\t\tout.nullValue();\n\t\t\treturn;\n\t\t}\n\t\tif (this.dateFormat == null) {\n\t\t\tout.value(value.getTime());\n\t\t\treturn;\n\t\t}\n\t\tout.value(SumkDate.format(value, this.dateFormat));\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/date/FullDateTimeFormater.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.date;\n\nimport org.yx.util.StringUtil;\nimport org.yx.util.SumkDate;\n\npublic final class FullDateTimeFormater implements SumkDateFormater {\n\tpublic static final FullDateTimeFormater inst = new FullDateTimeFormater();\n\n\tprivate FullDateTimeFormater() {\n\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic SumkDate parse(String text) {\n\t\tint dotIndex = text.length() - 1;\n\t\tfor (; dotIndex > 15; dotIndex--) {\n\t\t\tchar c = text.charAt(dotIndex);\n\t\t\tif (!StringUtil.isNumber(c)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tString textMil = text.substring(dotIndex + 1);\n\t\tint mils = Integer.parseInt(textMil);\n\t\tif (textMil.length() == 1) {\n\t\t\tmils *= 100;\n\t\t} else if (textMil.length() == 2) {\n\t\t\tmils *= 10;\n\t\t}\n\t\tint second = Integer.parseInt(text.substring(dotIndex - 2, dotIndex));\n\t\tint minute = Integer.parseInt(text.substring(dotIndex - 5, dotIndex - 3));\n\t\tint hour = Integer.parseInt(text.substring(dotIndex - 8, dotIndex - 6));\n\t\tint day = Integer.parseInt(text.substring(dotIndex - 11, dotIndex - 9));\n\t\tint month = Integer.parseInt(text.substring(dotIndex - 14, dotIndex - 12));\n\t\tint year = Integer.parseInt(text.substring(0, dotIndex - 15));\n\t\treturn SumkDate.of(year, month, day, hour, minute, second, mils);\n\t}\n\n\t@Override\n\tpublic boolean accept(String format) {\n\t\treturn format.length() == 23 && format.regionMatches(0, \"yyyy\", 0, 4) && format.regionMatches(5, \"MM\", 0, 2)\n\t\t\t\t&& format.regionMatches(8, \"dd\", 0, 2) && format.regionMatches(11, \"HH\", 0, 2)\n\t\t\t\t&& format.regionMatches(14, \"mm\", 0, 2) && format.regionMatches(17, \"ss\", 0, 2)\n\t\t\t\t&& format.endsWith(\"SSS\");\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/date/SumkDateFormater.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.date;\n\nimport org.yx.base.Ordered;\nimport org.yx.util.SumkDate;\n\npublic interface SumkDateFormater extends Ordered {\n\tSumkDate parse(String text);\n\n\tboolean accept(String format);\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/date/SumkDateQuery.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.date;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.OffsetDateTime;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.TemporalAccessor;\nimport java.time.temporal.TemporalQueries;\nimport java.time.temporal.TemporalQuery;\n\nimport org.yx.util.SumkDate;\n\npublic class SumkDateQuery implements TemporalQuery<SumkDate> {\n\tpublic static final SumkDateQuery inst = new SumkDateQuery();\n\n\t@Override\n\tpublic SumkDate queryFrom(TemporalAccessor temporal) {\n\t\tif (temporal instanceof LocalDateTime) {\n\t\t\treturn SumkDate.of((LocalDateTime) temporal);\n\t\t} else if (temporal instanceof ZonedDateTime) {\n\t\t\tLocalDateTime dt = ((ZonedDateTime) temporal).toLocalDateTime();\n\t\t\treturn SumkDate.of(dt);\n\t\t} else if (temporal instanceof OffsetDateTime) {\n\t\t\tLocalDateTime dt = ((OffsetDateTime) temporal).toLocalDateTime();\n\t\t\treturn SumkDate.of(dt);\n\t\t}\n\t\tLocalDate date = temporal.query(TemporalQueries.localDate());\n\t\tLocalTime time = temporal.query(TemporalQueries.localTime());\n\t\tif (date == null && time == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn SumkDate.of(date, time);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/date/TimeUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.date;\n\nimport java.sql.Time;\nimport java.sql.Timestamp;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.util.Date;\n\nimport org.yx.exception.SumkException;\nimport org.yx.util.SumkDate;\n\npublic class TimeUtil {\n\n\tpublic static final int DATETIME_CONVERT = 912753916;\n\n\tpublic static boolean isGenericDate(Class<?> type) {\n\t\treturn type == Date.class || type == java.sql.Date.class || type == Time.class || type == Timestamp.class\n\t\t\t\t|| type == LocalDate.class || type == LocalTime.class || type == LocalDateTime.class\n\t\t\t\t|| type == SumkDate.class;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T> T toType(Object v, Class<?> type, boolean failIfNotSupport) {\n\t\tif (v.getClass() == type) {\n\t\t\treturn (T) v;\n\t\t}\n\t\tif (type == SumkDate.class) {\n\t\t\treturn toSumkDate(v, failIfNotSupport);\n\t\t}\n\t\tif (v instanceof Date) {\n\t\t\treturn toType((Date) v, type, failIfNotSupport);\n\t\t}\n\t\tClass<?> sourceClz = v.getClass();\n\t\tif (LocalDateTime.class == sourceClz) {\n\t\t\treturn toType((LocalDateTime) v, type, failIfNotSupport);\n\t\t}\n\n\t\tif (LocalDate.class == sourceClz) {\n\t\t\treturn toType((LocalDate) v, type, failIfNotSupport);\n\t\t}\n\n\t\tif (LocalTime.class == sourceClz) {\n\t\t\treturn toType((LocalTime) v, type, failIfNotSupport);\n\t\t}\n\t\tif (SumkDate.class == sourceClz) {\n\t\t\treturn toType((SumkDate) v, type, failIfNotSupport);\n\t\t}\n\n\t\tif (failIfNotSupport || !isGenericDate(type)) {\n\t\t\tthrow new SumkException(1234345, type.getClass().getName() + \" cannot convert to \" + type.getName());\n\t\t}\n\t\treturn (T) v;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T> T toSumkDate(Object v, boolean failIfNotSupport) {\n\t\tif (v instanceof Date) {\n\t\t\treturn (T) SumkDate.of((Date) v);\n\t\t}\n\t\tif (v instanceof LocalDateTime) {\n\t\t\treturn (T) SumkDate.of((LocalDateTime) v);\n\t\t}\n\t\tif (v instanceof LocalDate) {\n\t\t\treturn (T) SumkDate.of((LocalDate) v, null);\n\t\t}\n\t\tif (v instanceof LocalTime) {\n\t\t\treturn (T) SumkDate.of(null, (LocalTime) v);\n\t\t}\n\t\tif (failIfNotSupport) {\n\t\t\tthrow new SumkException(63414353, v.getClass().getName() + \"is not supported Date type\");\n\t\t}\n\t\treturn (T) v;\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"deprecation\" })\n\tprivate static <T> T toType(SumkDate v, Class<?> type, boolean failIfNotSupport) {\n\t\tif (Date.class == type) {\n\t\t\treturn (T) v.toDate();\n\t\t}\n\t\tif (java.sql.Date.class == type) {\n\t\t\treturn (T) java.sql.Date.valueOf(v.toLocalDate());\n\t\t}\n\n\t\tif (Timestamp.class == type) {\n\t\t\treturn (T) v.toTimestamp();\n\t\t}\n\n\t\tif (Time.class == type) {\n\t\t\treturn (T) new Time(v.getHour(), v.getMinute(), v.getSecond());\n\t\t}\n\n\t\tif (LocalDate.class == type) {\n\t\t\treturn (T) v.toLocalDate();\n\t\t}\n\n\t\tif (LocalDateTime.class == type) {\n\t\t\treturn (T) v.toLocalDateTime();\n\t\t}\n\n\t\tif (LocalTime.class == type) {\n\t\t\treturn (T) v.toLocalTime();\n\t\t}\n\t\tif (failIfNotSupport || !isGenericDate(type)) {\n\t\t\tthrow new SumkException(63414353, type.getClass().getName() + \"is not supported Date type\");\n\t\t}\n\t\treturn (T) v;\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"deprecation\" })\n\tprivate static <T> T toType(Date v, Class<?> type, boolean failIfNotSupport) {\n\t\tlong time = v.getTime();\n\t\tif (Date.class == type) {\n\t\t\treturn (T) new Date(time);\n\t\t}\n\t\tif (java.sql.Date.class == type) {\n\t\t\tif (Time.class == v.getClass()) {\n\t\t\t\tthrow new SumkException(DATETIME_CONVERT, \"Time cannot convert to java.sql.Date\");\n\t\t\t}\n\t\t\treturn (T) new java.sql.Date(v.getYear(), v.getMonth(), v.getDate());\n\t\t}\n\n\t\tif (Timestamp.class == type) {\n\t\t\treturn (T) new Timestamp(time);\n\t\t}\n\n\t\tif (Time.class == type) {\n\t\t\tif (java.sql.Date.class == v.getClass()) {\n\t\t\t\tthrow new SumkException(DATETIME_CONVERT, \"java.sql.Date cannot convert to Time\");\n\t\t\t}\n\t\t\treturn (T) new Time(v.getHours(), v.getMinutes(), v.getSeconds());\n\t\t}\n\t\tSumkDate sumk = SumkDate.of(v);\n\n\t\tif (LocalDate.class == type) {\n\t\t\tif (Time.class == v.getClass()) {\n\t\t\t\tthrow new SumkException(DATETIME_CONVERT, \"Time cannot convert to LocalDate\");\n\t\t\t}\n\t\t\treturn (T) sumk.toLocalDate();\n\t\t}\n\n\t\tif (LocalDateTime.class == type) {\n\t\t\treturn (T) sumk.toLocalDateTime();\n\t\t}\n\n\t\tif (LocalTime.class == type) {\n\t\t\tif (java.sql.Date.class == v.getClass()) {\n\t\t\t\tthrow new SumkException(DATETIME_CONVERT, \"java.sql.Date cannot convert to LocalTime\");\n\t\t\t}\n\t\t\treturn (T) sumk.toLocalTime();\n\t\t}\n\t\tif (failIfNotSupport || !isGenericDate(type)) {\n\t\t\tthrow new SumkException(63414353, type.getClass().getName() + \"is not supported Date type\");\n\t\t}\n\t\treturn (T) v;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T> T toType(LocalDateTime v, Class<?> type, boolean failIfNotSupport) {\n\t\tif (Date.class == type) {\n\t\t\treturn (T) Date.from(v.atZone(ZoneId.systemDefault()).toInstant());\n\t\t}\n\t\tif (java.sql.Date.class == type) {\n\t\t\treturn (T) java.sql.Date.valueOf(v.toLocalDate());\n\t\t}\n\n\t\tif (Timestamp.class == type) {\n\t\t\treturn (T) Timestamp.valueOf(v);\n\t\t}\n\n\t\tif (Time.class == type) {\n\t\t\treturn (T) Time.valueOf(v.toLocalTime());\n\t\t}\n\n\t\tif (LocalDate.class == type) {\n\t\t\treturn (T) v.toLocalDate();\n\t\t}\n\n\t\tif (LocalTime.class == type) {\n\t\t\treturn (T) v.toLocalTime();\n\t\t}\n\n\t\tif (failIfNotSupport || !isGenericDate(type)) {\n\t\t\tthrow new SumkException(DATETIME_CONVERT, type.getClass().getName() + \"is not a supported Date type\");\n\t\t}\n\t\treturn (T) v;\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"deprecation\" })\n\tprivate static <T> T toType(LocalDate v, Class<?> type, boolean failIfNotSupport) {\n\t\tif (Date.class == type) {\n\t\t\treturn (T) new Date(v.getYear() - 1900, v.getMonthValue() - 1, v.getDayOfMonth());\n\t\t}\n\t\tif (java.sql.Date.class == type) {\n\t\t\treturn (T) java.sql.Date.valueOf(v);\n\t\t}\n\n\t\tif (Timestamp.class == type) {\n\t\t\tLocalDateTime dt = LocalDateTime.of(v, LocalTime.of(0, 0));\n\t\t\treturn (T) Timestamp.valueOf(dt);\n\t\t}\n\n\t\tif (Time.class == type) {\n\t\t\tthrow new SumkException(DATETIME_CONVERT, \"LocalDate cannot convert to Time\");\n\t\t}\n\n\t\tif (LocalDateTime.class == type) {\n\t\t\treturn (T) LocalDateTime.of(v, LocalTime.of(0, 0));\n\t\t}\n\n\t\tif (LocalTime.class == type) {\n\t\t\tthrow new SumkException(DATETIME_CONVERT, \"LocalDate cannot convert to LocalTime\");\n\t\t}\n\n\t\tif (failIfNotSupport || !isGenericDate(type)) {\n\t\t\tthrow new SumkException(DATETIME_CONVERT, type.getClass().getName() + \"is not a supported Date type\");\n\t\t}\n\t\treturn (T) v;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T> T toType(LocalTime v, Class<?> type, boolean failIfNotSupport) {\n\t\tif (Date.class == type) {\n\t\t\treturn (T) new Date(Time.valueOf(v).getTime());\n\t\t}\n\t\tif (java.sql.Date.class == type) {\n\t\t\tthrow new SumkException(DATETIME_CONVERT, \"LocalTime cannot convert to java.sql.Date\");\n\t\t}\n\n\t\tif (Timestamp.class == type) {\n\t\t\treturn (T) new Timestamp(Time.valueOf(v).getTime());\n\t\t}\n\n\t\tif (Time.class == type) {\n\t\t\treturn (T) Time.valueOf(v);\n\t\t}\n\n\t\tif (LocalDateTime.class == type) {\n\t\t\treturn (T) LocalDateTime.of(LocalDate.ofEpochDay(0), v);\n\t\t}\n\n\t\tif (LocalDate.class == type) {\n\t\t\tthrow new SumkException(DATETIME_CONVERT, \"LocalTime cannot convert to LocalDate\");\n\t\t}\n\n\t\tif (failIfNotSupport || !isGenericDate(type)) {\n\t\t\tthrow new SumkException(DATETIME_CONVERT, type.getClass().getName() + \"is not a supported Date type\");\n\t\t}\n\t\treturn (T) v;\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/matcher/BooleanMatcher.java",
    "content": "package org.yx.base.matcher;\n\nimport java.util.function.Predicate;\n\npublic enum BooleanMatcher implements Predicate<String> {\n\n\tFALSE(false), TRUE(true);\n\n\tprivate final boolean matched;\n\n\tprivate BooleanMatcher(boolean matched) {\n\t\tthis.matched = matched;\n\t}\n\n\t@Override\n\tpublic boolean test(String text) {\n\t\treturn matched;\n\t}\n\n\t@Override\n\tpublic Predicate<String> negate() {\n\t\treturn this.matched ? FALSE : TRUE;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/matcher/InOutMatcher.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.matcher;\n\nimport java.util.Objects;\nimport java.util.function.Predicate;\n\npublic class InOutMatcher implements Predicate<String> {\n\n\tprivate final Predicate<String> include;\n\tprivate final Predicate<String> exclude;\n\n\tInOutMatcher(Predicate<String> include, Predicate<String> exclude) {\n\t\tthis.include = Objects.requireNonNull(include);\n\t\tthis.exclude = Objects.requireNonNull(exclude);\n\t}\n\n\t@Override\n\tpublic boolean test(String t) {\n\t\tif (exclude.test(t)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn include.test(t);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"[include: \" + include + \", exclude: \" + exclude + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/matcher/Matchers.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.matcher;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.yx.base.matcher.WildcardMatcher.HeadAndTail;\nimport org.yx.log.RawLog;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic class Matchers {\n\n\t/**\n\t * 表达式的分隔符\n\t */\n\tpublic static final String SPLIT = \",\";\n\n\tpublic static final String WILDCARD = \"*\";\n\n\tpublic static Predicate<String> createWildcardMatcher(String patterns) {\n\t\treturn createWildcardMatcher(patterns, 1);\n\t}\n\n\tpublic static Predicate<String> createWildcardMatcher(String patterns, int minPatternLength) {\n\t\tif (patterns == null || patterns.isEmpty()) {\n\t\t\treturn BooleanMatcher.FALSE;\n\t\t}\n\t\tString[] array = StringUtil.toLatin(patterns).split(SPLIT);\n\t\treturn createWildcardMatcher(CollectionUtil.unmodifyList(array), minPatternLength);\n\t}\n\n\t/**\n\t * 创建表达式。这里的表达式不做全角半角处理，也不对逗号做分割，但允许包含null\n\t * \n\t * @param patterns         表达式字符串列表，支持为null。这里的每一个都是最小的表达式，不支持内部再含有逗号\n\t * @param minPatternLength 最小长度，会忽略pattern长度小于这个最小长度的\n\t * @return 表达式判断器\n\t */\n\tpublic static Predicate<String> createWildcardMatcher(Collection<String> patterns, int minPatternLength) {\n\t\tif (patterns == null || patterns.isEmpty()) {\n\t\t\treturn BooleanMatcher.FALSE;\n\t\t}\n\t\tSet<String> exact = new HashSet<>();\n\t\tSet<String> matchStart = new HashSet<>();\n\t\tSet<String> matchEnd = new HashSet<>();\n\t\tSet<String> matchContain = new HashSet<>();\n\t\tSet<HeadAndTail> headAndTailMatch = new HashSet<>();\n\t\tfinal String doubleWild = WILDCARD + WILDCARD;\n\t\tfor (String s : patterns) {\n\t\t\tif (s == null || (s = s.trim()).isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (s.length() < minPatternLength) {\n\t\t\t\tRawLog.warn(\"sumk.conf\", \"[\" + s + \"]的长度太短，被忽略.最小长度为:\" + minPatternLength);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (WILDCARD.equals(s) || doubleWild.equals(s)) {\n\t\t\t\treturn BooleanMatcher.TRUE;\n\t\t\t}\n\n\t\t\tint wildCount = s.length() - s.replace(WILDCARD, \"\").length();\n\t\t\tif (wildCount > 2) {\n\t\t\t\tRawLog.warn(\"sumk.conf\", \"[\" + s + \"]的*出现次数超过2次，将被忽略\");\n\n\t\t\t}\n\t\t\tif (wildCount == 0) {\n\t\t\t\texact.add(s);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tint firstIndex = s.indexOf(WILDCARD);\n\t\t\tif (wildCount == 1) {\n\t\t\t\tif (firstIndex == 0) {\n\t\t\t\t\tmatchEnd.add(s.substring(1));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (firstIndex == s.length() - 1) {\n\t\t\t\t\tmatchStart.add(s.substring(0, firstIndex));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tHeadAndTail h = new HeadAndTail(s.substring(0, firstIndex), s.substring(firstIndex + 1));\n\t\t\t\theadAndTailMatch.add(h);\n\t\t\t}\n\t\t\tif (wildCount == 2) {\n\t\t\t\tif (firstIndex == 0 && s.endsWith(WILDCARD)) {\n\t\t\t\t\tmatchContain.add(s.substring(1, s.length() - 1));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tRawLog.warn(\"sumk.conf\", \"[\" + s + \"]的*不出现了2次，但不在头尾，将被忽略\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t}\n\n\t\tif (exact.isEmpty() && matchStart.isEmpty() && matchEnd.isEmpty() && matchContain.isEmpty()\n\t\t\t\t&& headAndTailMatch.isEmpty()) {\n\t\t\treturn BooleanMatcher.FALSE;\n\t\t}\n\n\t\treturn new WildcardMatcher(exact, matchStart, matchEnd, matchContain, headAndTailMatch);\n\t}\n\n\t/**\n\t * 创建InOutMatcher、BooleanMatcher或其它Matcher\n\t * \n\t * @param include 可以匹配的Predicate,不能为null\n\t * @param exclude 被排除的Predicate,不能为null\n\t * @return 返回值有可能是InOutMatcher类型，也有可能是BooleanMatcher\n\t */\n\tpublic static Predicate<String> includeAndExclude(Predicate<String> include, Predicate<String> exclude) {\n\t\tif (exclude == BooleanMatcher.FALSE) {\n\t\t\treturn include;\n\t\t}\n\t\tif (include == BooleanMatcher.FALSE || exclude == BooleanMatcher.TRUE) {\n\t\t\treturn BooleanMatcher.FALSE;\n\t\t}\n\t\treturn new InOutMatcher(include, exclude);\n\t}\n\n\tpublic static Predicate<String> includeAndExclude(String include, String exclude) {\n\t\treturn includeAndExclude(createWildcardMatcher(include), createWildcardMatcher(exclude));\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/matcher/WildcardMatcher.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.matcher;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.yx.base.ItemJoiner;\nimport org.yx.util.CollectionUtil;\n\n/**\n * 或类型的匹配，只要能匹配中一个条件，就认为匹配成功\n *\n */\npublic class WildcardMatcher implements Predicate<String> {\n\n\tprivate final Set<String> exacts;\n\n\tprivate final String[] matchStarts;\n\n\tprivate final String[] matchEnds;\n\n\tprivate final String[] contains;\n\n\tprivate final HeadAndTail[] headTails;\n\n\tWildcardMatcher(Set<String> exacts, Set<String> matchStart, Set<String> matchEnd, Set<String> matchContain,\n\t\t\tSet<HeadAndTail> headAndTailMatch) {\n\t\tthis.exacts = CollectionUtil.isEmpty(exacts) ? null : new HashSet<>(exacts);\n\t\tthis.matchStarts = toArray(matchStart);\n\t\tthis.matchEnds = toArray(matchEnd);\n\t\tthis.contains = toArray(matchContain);\n\t\tthis.headTails = CollectionUtil.isEmpty(headAndTailMatch) ? null\n\t\t\t\t: headAndTailMatch.toArray(new HeadAndTail[headAndTailMatch.size()]);\n\t}\n\n\tprivate String[] toArray(Collection<String> src) {\n\t\tif (CollectionUtil.isEmpty(src)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn src.toArray(new String[src.size()]);\n\t}\n\n\t@Override\n\tpublic boolean test(String text) {\n\n\t\tif (this.exacts != null && this.exacts.contains(text)) {\n\n\t\t\treturn true;\n\t\t}\n\n\t\tif (this.matchStarts != null) {\n\t\t\tfor (String start : this.matchStarts) {\n\t\t\t\tif (text.startsWith(start)) {\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.matchEnds != null) {\n\t\t\tfor (String end : this.matchEnds) {\n\t\t\t\tif (text.endsWith(end)) {\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.contains != null) {\n\t\t\tfor (String c : this.contains) {\n\t\t\t\tif (text.contains(c)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.headTails != null) {\n\t\t\tfor (HeadAndTail ht : this.headTails) {\n\t\t\t\tif (ht.test(text)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic Collection<String> exacts() {\n\t\treturn exacts == null ? null : Collections.unmodifiableSet(exacts);\n\t}\n\n\tpublic List<String> matchStarts() {\n\t\treturn CollectionUtil.unmodifyList(matchStarts);\n\t}\n\n\tpublic List<String> matchEnds() {\n\t\treturn CollectionUtil.unmodifyList(matchEnds);\n\t}\n\n\tpublic List<String> contains() {\n\t\treturn CollectionUtil.unmodifyList(contains);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tItemJoiner join = new ItemJoiner(\",\");\n\t\tif (exacts != null) {\n\t\t\tjoin.item().append(\"exacts:\").append(exacts);\n\t\t}\n\t\tif (matchStarts != null) {\n\t\t\tjoin.item().append(\"matchStarts:\").append(Arrays.toString(matchStarts));\n\t\t}\n\t\tif (matchEnds != null) {\n\t\t\tjoin.item().append(\"matchEnds:\").append(Arrays.toString(matchEnds));\n\t\t}\n\t\tif (contains != null) {\n\t\t\tjoin.item().append(\"contains:\").append(Arrays.toString(contains));\n\t\t}\n\t\tif (headTails != null) {\n\t\t\tjoin.item().append(\"headTails:\").append(Arrays.toString(headTails));\n\t\t}\n\t\treturn join.toString();\n\t}\n\n\tpublic static final class HeadAndTail implements Predicate<String> {\n\t\tfinal String head;\n\t\tfinal String tail;\n\n\t\tpublic HeadAndTail(String head, String tail) {\n\t\t\tthis.head = Objects.requireNonNull(head);\n\t\t\tthis.tail = Objects.requireNonNull(tail);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean test(String text) {\n\t\t\tif (text.length() < head.length() + tail.length()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn text.startsWith(head) && text.endsWith(tail);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"HeadAndTail [head=\" + head + \", tail=\" + tail + \"]\";\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/scaner/ClassScaner.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.scaner;\n\nimport java.lang.reflect.Modifier;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.yx.util.Loader;\n\npublic final class ClassScaner {\n\n\tprivate static Function<Collection<String>, Collection<String>> scaner = new FileNameScaner(\".class\");\n\n\tpublic static Function<Collection<String>, Collection<String>> getScaner() {\n\t\treturn scaner;\n\t}\n\n\tpublic static void setScaner(Function<Collection<String>, Collection<String>> scaner) {\n\t\tClassScaner.scaner = Objects.requireNonNull(scaner);\n\t}\n\n\tpublic static <T> Set<Class<? extends T>> subClassesInSameOrSubPackage(Class<T> baseClz)\n\t\t\tthrows ClassNotFoundException {\n\t\treturn list(baseClz.getPackage().getName(), baseClz);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T> Set<Class<? extends T>> list(String packageName, Class<T> baseClz) throws ClassNotFoundException {\n\t\tCollection<String> clzNames = listClasses(Collections.singletonList(packageName));\n\t\tif (clzNames == null || clzNames.isEmpty()) {\n\t\t\treturn Collections.emptySet();\n\t\t}\n\t\tSet<String> names = new HashSet<>(clzNames);\n\t\tSet<Class<? extends T>> set = new HashSet<>();\n\t\tfor (String className : names) {\n\t\t\tClass<?> clz = Loader.loadClass(className);\n\t\t\tif (baseClz.isAssignableFrom(clz) && (!clz.isInterface())\n\t\t\t\t\t&& ((clz.getModifiers() & Modifier.ABSTRACT) == 0)) {\n\t\t\t\tset.add((Class<? extends T>) clz);\n\t\t\t}\n\t\t}\n\t\treturn set;\n\t}\n\n\tpublic static Collection<String> listClasses(Collection<String> packageNames) {\n\t\treturn scaner.apply(packageNames);\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/scaner/FileNameScaner.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.scaner;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.JarURLConnection;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\nimport org.slf4j.Logger;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\npublic final class FileNameScaner implements Function<Collection<String>, Collection<String>> {\n\tprivate final String subfix;\n\n\tpublic FileNameScaner(String subfix) {\n\t\tthis.subfix = Objects.requireNonNull(subfix);\n\t}\n\n\t@Override\n\tpublic Collection<String> apply(Collection<String> packageNames) {\n\t\tLogger log = Logs.system();\n\t\tSet<String> classNameSet = new HashSet<>(240);\n\t\tif (packageNames == null || packageNames.isEmpty()) {\n\t\t\treturn classNameSet;\n\t\t}\n\t\tString packagePath;\n\t\tFile file;\n\t\tURL url;\n\t\tEnumeration<URL> eUrl;\n\t\tfor (String packageName : packageNames) {\n\t\t\tpackagePath = StringUtil.toLatin(packageName).trim().replace('.', '/');\n\t\t\tif (packagePath.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!packagePath.endsWith(\"/\")) {\n\t\t\t\tpackagePath += \"/\";\n\t\t\t}\n\t\t\ttry {\n\t\t\t\teUrl = Loader.getResources(packagePath);\n\t\t\t\twhile (eUrl.hasMoreElements()) {\n\t\t\t\t\turl = eUrl.nextElement();\n\t\t\t\t\tlog.debug(\"find {}\", url.getFile());\n\t\t\t\t\tif (JarFileUtil.URL_PROTOCOL_JAR.equals(url.getProtocol())\n\t\t\t\t\t\t\t|| JarFileUtil.URL_PROTOCOL_ZIP.equals(url.getProtocol())) {\n\t\t\t\t\t\tthis.findClassInJar(classNameSet, url, packagePath);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfile = new File(url.toURI());\n\t\t\t\t\tif (!file.exists()) {\n\t\t\t\t\t\tthrow new SumkException(9723423, file.getAbsolutePath() + \" is not a file\");\n\t\t\t\t\t}\n\t\t\t\t\tthis.parseFile(classNameSet, file, packagePath);\n\t\t\t\t}\n\t\t\t} catch (Exception ex) {\n\t\t\t\tlog.error(\"parse \" + packageName + \"failed\", ex);\n\t\t\t\tthrow new SumkException(23423, ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n\t\treturn classNameSet;\n\t}\n\n\tprivate void parseFile(Collection<String> classNameCol, final File root, final String packagePath) {\n\t\tFile[] subFiles = root.listFiles();\n\t\tif (subFiles == null || subFiles.length == 0) {\n\t\t\treturn;\n\t\t}\n\t\tfor (File file : subFiles) {\n\t\t\tif (file.isDirectory()) {\n\t\t\t\tthis.parseFile(classNameCol, file, packagePath);\n\t\t\t} else if (file.getName().endsWith(subfix)) {\n\t\t\t\tString absolutePath = file.getAbsolutePath().replace('\\\\', '/');\n\t\t\t\tint index = absolutePath.indexOf('/' + packagePath) + 1;\n\t\t\t\tif (index < 1) {\n\t\t\t\t\tLogs.system().error(absolutePath + \" donot contain \" + packagePath);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\taddClz(classNameCol, absolutePath.substring(index));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void addClz(Collection<String> classNameList, String classPath) {\n\t\tLogger log = Logs.system();\n\t\tif (!classPath.endsWith(subfix) || classPath.contains(\"$\")) {\n\t\t\tif (log.isTraceEnabled()) {\n\t\t\t\tlog.trace(\"文件{}不满足条件\", classPath);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tString className = classPath.substring(0, classPath.length() - this.subfix.length()).replace('/', '.');\n\t\tif (!classNameList.contains(className)) {\n\t\t\tclassNameList.add(className);\n\t\t}\n\t}\n\n\tprivate void findClassInJar(Collection<String> classNameList, URL url, String packagePath) throws IOException {\n\t\tLogger log = Logs.system();\n\t\tJarFile jarFile = null;\n\t\ttry {\n\n\t\t\tURLConnection conn = url.openConnection();\n\t\t\tif (!JarURLConnection.class.isInstance(conn)) {\n\t\t\t\tlog.error(\"the connection of {} is {}\", url.getPath(), conn.getClass().getName());\n\t\t\t\tthrow new SumkException(25345643, conn.getClass().getName() + \" is not JarURLConnection\");\n\t\t\t}\n\t\t\tjarFile = ((JarURLConnection) conn).getJarFile();\n\t\t\tEnumeration<JarEntry> jarEntryEnum = jarFile.entries();\n\t\t\twhile (jarEntryEnum.hasMoreElements()) {\n\t\t\t\tString entityName = jarEntryEnum.nextElement().getName();\n\t\t\t\tif (entityName.startsWith(\"/\")) {\n\t\t\t\t\tentityName = entityName.substring(1);\n\t\t\t\t}\n\t\t\t\tif (entityName.startsWith(packagePath)) {\n\t\t\t\t\taddClz(classNameList, entityName);\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isTraceEnabled()) {\n\t\t\t\t\t\tlog.trace(\"{}不满足条件\", entityName);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tif (jarFile != null) {\n\t\t\t\tjarFile.close();\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/scaner/JarFileUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.scaner;\n\nimport java.io.IOException;\nimport java.net.JarURLConnection;\nimport java.net.URL;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Predicate;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\nimport org.yx.util.IOUtil;\n\npublic class JarFileUtil {\n\tpublic static final String URL_PROTOCOL_JAR = \"jar\";\n\n\tpublic static final String URL_PROTOCOL_ZIP = \"zip\";\n\n\tpublic static final String JAR_URL_SEPARATOR = \"!/\";\n\n\tpublic static boolean isJarURL(URL url) {\n\t\tString protocol = url.getProtocol();\n\t\treturn URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_ZIP.equals(protocol);\n\t}\n\n\tpublic static Map<String, byte[]> exactResourcesInJar(URL url, Predicate<String> tester) throws IOException {\n\t\tMap<String, byte[]> map = new HashMap<>();\n\t\tJarFile jarFile = null;\n\t\ttry {\n\t\t\tString prefix = \"\";\n\t\t\tString urlPath = url.getPath();\n\t\t\tint separatorIndex = urlPath.lastIndexOf(JAR_URL_SEPARATOR);\n\t\t\tif (separatorIndex != -1 && separatorIndex + JAR_URL_SEPARATOR.length() < urlPath.length()) {\n\t\t\t\tprefix = urlPath.substring(separatorIndex + JAR_URL_SEPARATOR.length());\n\t\t\t\tif (!prefix.endsWith(\"/\")) {\n\t\t\t\t\tprefix += \"/\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tjarFile = ((JarURLConnection) url.openConnection()).getJarFile();\n\t\t\tEnumeration<JarEntry> jarEntryEnum = jarFile.entries();\n\t\t\twhile (jarEntryEnum.hasMoreElements()) {\n\t\t\t\tJarEntry entry = jarEntryEnum.nextElement();\n\t\t\t\tString entityName = entry.getName();\n\t\t\t\tif (entry.isDirectory() || !entityName.startsWith(prefix)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (tester != null && !tester.test(entityName)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbyte[] bs = IOUtil.readAllBytes(jarFile.getInputStream(entry), true);\n\t\t\t\tmap.put(entityName, bs);\n\t\t\t}\n\t\t} finally {\n\t\t\tif (jarFile != null) {\n\t\t\t\tjarFile.close();\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/sumk/UnmodifiableArrayList.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.sumk;\n\nimport java.io.Serializable;\nimport java.util.AbstractList;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.Objects;\nimport java.util.RandomAccess;\nimport java.util.Spliterator;\nimport java.util.Spliterators;\nimport java.util.function.Consumer;\n\npublic final class UnmodifiableArrayList<E> extends AbstractList<E> implements RandomAccess, Serializable {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate final Object[] elements;\n\n\tpublic UnmodifiableArrayList(E[] array) {\n\t\telements = Objects.requireNonNull(array);\n\t}\n\n\tpublic UnmodifiableArrayList(Collection<E> col) {\n\n\t\tthis.elements = col.toArray();\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn elements.length;\n\t}\n\n\t@Override\n\tpublic Object[] toArray() {\n\t\treturn elements.clone();\n\t}\n\n\t@Override\n\tpublic <T> T[] toArray(T[] a) {\n\t\tSystem.arraycopy(this.elements, 0, a, 0, size());\n\t\treturn a;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic E get(int index) {\n\t\treturn (E) elements[index];\n\t}\n\n\t@Override\n\tpublic int indexOf(Object o) {\n\t\tObject[] a = this.elements;\n\t\tif (o == null) {\n\t\t\tfor (int i = 0; i < a.length; i++)\n\t\t\t\tif (a[i] == null)\n\t\t\t\t\treturn i;\n\t\t} else {\n\t\t\tfor (int i = 0; i < a.length; i++)\n\t\t\t\tif (o.equals(a[i]))\n\t\t\t\t\treturn i;\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic boolean contains(Object o) {\n\t\treturn indexOf(o) != -1;\n\t}\n\n\t@Override\n\tpublic Spliterator<E> spliterator() {\n\t\treturn Spliterators.spliterator(elements, Spliterator.ORDERED);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic void forEach(Consumer<? super E> action) {\n\t\tObjects.requireNonNull(action);\n\t\tfor (Object e : elements) {\n\t\t\taction.accept((E) e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void sort(Comparator<? super E> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/sumk/UnsafeByteArrayOutputStream.java",
    "content": "package org.yx.base.sumk;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Arrays;\n\npublic class UnsafeByteArrayOutputStream extends OutputStream {\n\n\tprivate byte[] buf;\n\n\tprivate int count;\n\n\tpublic UnsafeByteArrayOutputStream(int size) {\n\t\tif (size < 0) {\n\t\t\tthrow new IllegalArgumentException(\"Negative initial size: \" + size);\n\t\t}\n\t\tbuf = new byte[size];\n\t}\n\n\tprivate void ensureCapacity(int minCapacity) {\n\t\tif (minCapacity - buf.length > 0)\n\t\t\tgrow(minCapacity);\n\t}\n\n\tprivate static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;\n\n\tprivate void grow(int minCapacity) {\n\t\tint oldCapacity = buf.length;\n\t\tint newCapacity = oldCapacity << 1;\n\t\tif (newCapacity - minCapacity < 0)\n\t\t\tnewCapacity = minCapacity;\n\t\tif (newCapacity - MAX_ARRAY_SIZE > 0)\n\t\t\tnewCapacity = hugeCapacity(minCapacity);\n\t\tbuf = Arrays.copyOf(buf, newCapacity);\n\t}\n\n\tprivate static int hugeCapacity(int minCapacity) {\n\t\tif (minCapacity < 0)\n\t\t\tthrow new OutOfMemoryError();\n\t\treturn (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;\n\t}\n\n\tpublic void write(int b) {\n\t\tensureCapacity(count + 1);\n\t\tbuf[count] = (byte) b;\n\t\tcount += 1;\n\t}\n\n\tpublic void write(byte b[], int off, int len) {\n\t\tif ((off < 0) || (off > b.length) || (len < 0) || ((off + len) - b.length > 0)) {\n\t\t\tthrow new IndexOutOfBoundsException();\n\t\t}\n\t\tensureCapacity(count + len);\n\t\tSystem.arraycopy(b, off, buf, count, len);\n\t\tcount += len;\n\t}\n\n\tpublic void writeTo(OutputStream out) throws IOException {\n\t\tout.write(buf, 0, count);\n\t}\n\n\tpublic void reset() {\n\t\tcount = 0;\n\t}\n\n\tpublic byte[] toByteArray() {\n\t\treturn Arrays.copyOf(buf, count);\n\t}\n\n\tpublic int size() {\n\t\treturn count;\n\t}\n\n\tpublic String toString() {\n\t\treturn new String(buf, 0, count);\n\t}\n\n\tpublic String toString(String charsetName) throws UnsupportedEncodingException {\n\t\treturn new String(buf, 0, count, charsetName);\n\t}\n\n\tpublic byte[] extractHttpBodyData() {\n\t\tint dataLength = this.count;\n\t\tif (dataLength == 0) {\n\t\t\treturn new byte[0];\n\t\t}\n\t\tbyte[] bs = this.buf;\n\n\t\tif (dataLength > 4 && bs[0] == 100 && bs[1] == 97 && bs[2] == 116 && bs[3] == 97 && bs[4] == 61) {\n\t\t\tbyte[] temp = new byte[dataLength - 5];\n\t\t\tSystem.arraycopy(bs, 5, temp, 0, temp.length);\n\t\t\treturn temp;\n\t\t}\n\n\t\treturn dataLength == bs.length ? bs : Arrays.copyOf(bs, dataLength);\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/sumk/UnsafeStringWriter.java",
    "content": "package org.yx.base.sumk;\n\nimport java.io.IOException;\nimport java.io.Writer;\n\npublic class UnsafeStringWriter extends Writer {\n\n\tprivate final StringBuilder buf;\n\n\tpublic UnsafeStringWriter() {\n\t\tthis(32);\n\t}\n\n\tpublic UnsafeStringWriter(int initialSize) {\n\t\tif (initialSize < 0) {\n\t\t\tthrow new IllegalArgumentException(\"Negative buffer size\");\n\t\t}\n\t\tbuf = new StringBuilder(initialSize);\n\t\tlock = buf;\n\t}\n\n\tpublic UnsafeStringWriter(StringBuilder sb) {\n\t\tthis.buf = sb;\n\t\tthis.lock = buf;\n\t}\n\n\tpublic void write(int c) {\n\t\tbuf.append((char) c);\n\t}\n\n\tpublic void write(char cbuf[], int off, int len) {\n\t\tif ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) {\n\t\t\tthrow new IndexOutOfBoundsException();\n\t\t} else if (len == 0) {\n\t\t\treturn;\n\t\t}\n\t\tbuf.append(cbuf, off, len);\n\t}\n\n\tpublic void write(String str) {\n\t\tbuf.append(str);\n\t}\n\n\tpublic void write(String str, int off, int len) {\n\t\tbuf.append(str.substring(off, off + len));\n\t}\n\n\tpublic UnsafeStringWriter append(CharSequence csq) {\n\t\tif (csq == null) {\n\t\t\twrite(\"null\");\n\t\t} else {\n\t\t\tbuf.append(csq);\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic UnsafeStringWriter append(CharSequence csq, int start, int end) {\n\t\tCharSequence cs = (csq == null ? \"null\" : csq);\n\t\twrite(cs.subSequence(start, end).toString());\n\t\treturn this;\n\t}\n\n\tpublic UnsafeStringWriter append(char c) {\n\t\twrite(c);\n\t\treturn this;\n\t}\n\n\tpublic String toString() {\n\t\treturn buf.toString();\n\t}\n\n\tpublic StringBuilder getBuffer() {\n\t\treturn buf;\n\t}\n\n\tpublic void flush() {\n\t}\n\n\t/**\n\t * 空方法，close后不影响使用\n\t */\n\tpublic void close() throws IOException {\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/sumk/map/ListEntrySet.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.sumk.map;\n\nimport java.util.AbstractSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map.Entry;\nimport java.util.Objects;\n\npublic class ListEntrySet<K, V> extends AbstractSet<Entry<K, V>> {\n\n\tprotected final List<Entry<K, V>> list;\n\n\tpublic ListEntrySet(List<Entry<K, V>> list) {\n\t\tthis.list = Objects.requireNonNull(list);\n\t}\n\n\t@Override\n\tpublic boolean add(Entry<K, V> e) {\n\t\tEntry<K, V> old = this.getByKey(e.getKey());\n\t\tif (old != null) {\n\t\t\told.setValue(e.getValue());\n\t\t\treturn true;\n\t\t}\n\t\treturn list.add(e);\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn list.size();\n\t}\n\n\tpublic Entry<K, V> getByKey(K key) {\n\t\tint index = this.indexKey(key);\n\t\tif (index < 0) {\n\t\t\treturn null;\n\t\t}\n\t\treturn list.get(index);\n\t}\n\n\tpublic int indexKey(K key) {\n\t\tfinal int size = list.size();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tEntry<K, V> en = list.get(i);\n\t\t\tif (Objects.equals(en.getKey(), key)) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic Iterator<Entry<K, V>> iterator() {\n\t\treturn list.iterator();\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/sumk/map/ListMap.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.sumk.map;\n\nimport java.io.Serializable;\nimport java.util.AbstractMap;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\n/**\n * 支持key、value为null。 适用于很少调用get、remove甚至put的场景。或者map很小。\n * 它的优势是占用内存小，并且iterator的性能比HashMap还好\n */\npublic class ListMap<K, V> extends AbstractMap<K, V> implements Serializable {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate final ListEntrySet<K, V> data;\n\n\tprotected ListMap(ListEntrySet<K, V> data) {\n\t\tthis.data = Objects.requireNonNull(data);\n\t}\n\n\tpublic ListMap() {\n\t\tthis.data = new ListEntrySet<K, V>(new ArrayList<>());\n\t}\n\n\tpublic ListMap(int initialCapacity) {\n\t\tthis.data = new ListEntrySet<K, V>(new ArrayList<>(initialCapacity));\n\t}\n\n\tpublic ListMap(Map<K, V> map) {\n\t\tthis(map, 0);\n\t}\n\n\tpublic ListMap(Map<K, V> map, int estimateGrowSize) {\n\t\tList<Entry<K, V>> list = new ArrayList<>(map.size() + estimateGrowSize);\n\t\tfor (Entry<K, V> en : map.entrySet()) {\n\t\t\tlist.add(new AbstractMap.SimpleEntry<>(en));\n\t\t}\n\t\tthis.data = new ListEntrySet<K, V>(list);\n\t}\n\n\t@Override\n\tpublic Set<Entry<K, V>> entrySet() {\n\t\treturn data;\n\t}\n\n\t@Override\n\tpublic V put(K key, V value) {\n\t\tEntry<K, V> old = data.getByKey(key);\n\t\tV v = old != null ? old.getValue() : null;\n\t\tdata.add(new AbstractMap.SimpleEntry<>(key, value));\n\t\treturn v;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/sumk/map/UnmodifiableListMap.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.sumk.map;\n\nimport java.io.Serializable;\nimport java.util.AbstractMap;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.yx.util.CollectionUtil;\n\n/**\n * 支持key、value为null\n */\npublic class UnmodifiableListMap<K, V> extends AbstractMap<K, V> implements Serializable {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate final ListEntrySet<K, V> data;\n\n\tpublic UnmodifiableListMap(Map<K, V> map) {\n\t\tList<Entry<K, V>> list = new ArrayList<>(map.size());\n\t\tfor (Entry<K, V> en : map.entrySet()) {\n\t\t\tlist.add(new AbstractMap.SimpleImmutableEntry<>(en));\n\t\t}\n\t\tthis.data = new ListEntrySet<>(CollectionUtil.unmodifyList(list));\n\t}\n\n\t@Override\n\tpublic Set<Entry<K, V>> entrySet() {\n\t\treturn data;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/thread/PriorityRunnable.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.thread;\n\nimport java.util.Objects;\n\nimport org.yx.log.Log;\n\npublic class PriorityRunnable implements Runnable {\n\n\tprivate int priority;\n\tprivate final Runnable target;\n\tprivate final int threshold;\n\n\tpublic PriorityRunnable(Runnable target, int priority, int threshold) {\n\t\tthis.target = Objects.requireNonNull(target, \"target is null\");\n\t\tthis.priority = priority;\n\t\tthis.threshold = threshold;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tif (this.priority < threshold) {\n\t\t\tString msg = new StringBuilder().append(\"Task \").append(toString()).append(\" discarded, because of \")\n\t\t\t\t\t.append(priority).append(\" lower than \").append(threshold).toString();\n\t\t\tLog.get(\"sumk.thread\").warn(msg);\n\t\t\treturn;\n\t\t}\n\t\ttarget.run();\n\t}\n\n\tint priority() {\n\t\treturn priority;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/thread/SumkExecutorService.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.thread;\n\nimport java.util.concurrent.ExecutorService;\n\npublic interface SumkExecutorService extends ExecutorService, ThresholdExecutor {\n\n\tint getCorePoolSize();\n\n\tvoid setCorePoolSize(int size);\n\n\tint getMaximumPoolSize();\n\n\tvoid setMaximumPoolSize(int size);\n\n\tboolean allowsCoreThreadTimeOut();\n\n\tvoid allowCoreThreadTimeOut(boolean allowCoreTimeout);\n\n\tint getPoolSize();\n\n\tint getQueued();\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/thread/ThreadPools.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.thread;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.RejectedExecutionException;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Log;\nimport org.yx.util.SumkThreadPool;\n\npublic class ThreadPools {\n\n\tpublic static final SumkExecutorService DEFAULT_EXECUTOR = ThreadPools.create(\"sumk\", 50, 500,\n\t\t\tInteger.getInteger(\"sumk.thread.pool.keepAliveTime\", 30000));\n\n\tpublic static SumkExecutorService create(String name, int core, int max, int aliveTime) {\n\t\tAbortPolicyQueue abortPolicyQueue = new AbortPolicyQueue(AppInfo.getInt(\"sumk.threadpool.maxqueue\", 100000),\n\t\t\t\tcore);\n\t\tThresholdThreadPool pool = new ThresholdThreadPool(core, max, aliveTime, TimeUnit.MILLISECONDS,\n\t\t\t\tabortPolicyQueue, SumkThreadPool.createThreadFactory(name + \"-\"), abortPolicyQueue, 0);\n\t\tpool.setCorePoolSize(core);\n\t\tabortPolicyQueue.pool = pool;\n\t\treturn pool;\n\t}\n\n\tprivate static final class AbortPolicyQueue extends LinkedBlockingQueue<Runnable>\n\t\t\timplements RejectedExecutionHandler {\n\n\t\tprivate static final long serialVersionUID = 1L;\n\t\tprivate SumkExecutorService pool;\n\t\tprivate int softCapacity;\n\n\t\tAbortPolicyQueue(int capacity, int softCapacity) {\n\t\t\tsuper(capacity);\n\t\t\tthis.softCapacity = softCapacity;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean offer(Runnable r) {\n\t\t\tif (this.size() > softCapacity) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn super.offer(r);\n\t\t}\n\n\t\t@Override\n\t\tpublic void rejectedExecution(Runnable r, ThreadPoolExecutor e) {\n\t\t\tif (pool.isShutdown()) {\n\t\t\t\tthrow new RejectedExecutionException(\"Task \" + r.toString() + \" rejected from \" + e.toString()\n\t\t\t\t\t\t+ \",because of Thread pool shutdowned\");\n\t\t\t}\n\n\t\t\tif (PriorityRunnable.class == r.getClass() && ((PriorityRunnable) r).priority() < pool.threshold()) {\n\t\t\t\tString msg = new StringBuilder(\"Task \").append(r.toString()).append(\" rejected from \")\n\t\t\t\t\t\t.append(e.toString()).append(\", because of \").append(((PriorityRunnable) r).priority())\n\t\t\t\t\t\t.append(\" lower than \").append(pool.threshold()).toString();\n\t\t\t\tthrow new RejectedExecutionException(msg);\n\t\t\t}\n\t\t\tint waiting = this.size();\n\t\t\tif (waiting % 50 == 0 && Log.get(\"sumk.thread\").isWarnEnabled()) {\n\t\t\t\tLog.get(\"sumk.thread\").warn(\"task is busy,waiting size:{}\", waiting);\n\t\t\t}\n\t\t\tif (!super.offer(r)) {\n\t\t\t\tthrow new RejectedExecutionException(\"Task \" + r.toString() + \" rejected from \" + e.toString());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class ThresholdThreadPool extends ThreadPoolExecutor implements SumkExecutorService {\n\n\t\tprivate int threshold;\n\n\t\tpublic ThresholdThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,\n\t\t\t\tBlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler,\n\t\t\t\tint threshold) {\n\t\t\tsuper(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);\n\t\t\tthis.threshold = threshold;\n\t\t}\n\n\t\t@Override\n\t\tpublic int threshold() {\n\t\t\treturn threshold;\n\t\t}\n\n\t\t@Override\n\t\tpublic void threshold(int threshold) {\n\t\t\tthis.threshold = threshold;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setThreadFactory(ThreadFactory threadFactory) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void setCorePoolSize(int corePoolSize) {\n\t\t\tsuper.setCorePoolSize(corePoolSize);\n\t\t\tAbortPolicyQueue q = (AbortPolicyQueue) this.getQueue();\n\t\t\tq.softCapacity = (int) (corePoolSize * 0.7);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getQueued() {\n\t\t\treturn this.getQueue().size();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/base/thread/ThresholdExecutor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.base.thread;\n\npublic interface ThresholdExecutor {\n\n\tint threshold();\n\n\tvoid threshold(int threshold);\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/AbsoluteXmlFilesLoader.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.util.FileUtil;\n\npublic class AbsoluteXmlFilesLoader extends AbstractFilesLoader {\n\n\tpublic AbsoluteXmlFilesLoader(String rootUri, String subfix) {\n\t\tsuper(rootUri, subfix);\n\t}\n\n\t@Override\n\tpublic Map<String, byte[]> openResources(String db) throws Exception {\n\t\tList<File> files = new ArrayList<>();\n\t\tFile root = new File(rootUri);\n\t\tif (!root.exists() || !root.isDirectory()) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMap<String, byte[]> map = new HashMap<>();\n\t\tFileUtil.listAllSubFiles(files, root);\n\t\tList<FileModifyTime> timeList = new ArrayList<>(files.size());\n\t\tfor (int i = 0; i < files.size(); i++) {\n\t\t\tFile f = files.get(i);\n\t\t\tif (!f.getName().endsWith(\".xml\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmap.put(f.getName(), Files.readAllBytes(f.toPath()));\n\t\t\ttimeList.add(new FileModifyTime(f.getAbsolutePath(), f.lastModified()));\n\t\t}\n\t\tthis.times = timeList.toArray(new FileModifyTime[timeList.size()]);\n\t\treturn map;\n\t}\n}"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/AbstractFilesLoader.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.io.File;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Consumer;\n\nimport org.yx.util.Task;\n\npublic abstract class AbstractFilesLoader implements MultiResourceLoader, Runnable {\n\tprotected Consumer<MultiResourceLoader> consumer;\n\tprotected FileModifyTime[] times;\n\tprotected final String rootUri;\n\tprotected final String subfix;\n\n\tpublic AbstractFilesLoader(String rootUri, String subfix) {\n\t\tthis.rootUri = Objects.requireNonNull(rootUri);\n\t\tthis.subfix = \"\".equals(subfix) ? null : subfix;\n\t}\n\n\t@Override\n\tpublic synchronized boolean startListen(Consumer<MultiResourceLoader> consumer) {\n\t\tif (this.consumer != null) {\n\t\t\treturn false;\n\t\t}\n\t\tthis.consumer = consumer;\n\t\tTask.scheduleAtFixedRate(this, 60, getRefreshDelay(), TimeUnit.SECONDS);\n\t\treturn true;\n\t}\n\n\tprotected long getRefreshDelay() {\n\t\treturn AppInfo.getLong(\"sumk.file.refresh.delay\", 60);\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tFileModifyTime[] times = this.times;\n\t\tif (times == null || times.length == 0) {\n\t\t\treturn;\n\t\t}\n\t\tboolean modified = false;\n\t\tfor (FileModifyTime ft : times) {\n\t\t\tFile f = new File(ft.file);\n\t\t\tif (f.lastModified() > ft.lastModify) {\n\t\t\t\tmodified = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (modified) {\n\t\t\tconsumer.accept(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/AbstractRefreshableSystemConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.util.function.Consumer;\n\nimport org.yx.base.StartOnceLifecycle;\nimport org.yx.log.RawLog;\n\npublic abstract class AbstractRefreshableSystemConfig extends StartOnceLifecycle implements RefreshableSystemConfig {\n\tprotected static final String LOG_NAME = \"sumk.conf\";\n\tprotected Consumer<RefreshableSystemConfig> observer;\n\n\t@Override\n\tpublic void setConsumer(Consumer<RefreshableSystemConfig> observer) {\n\t\tthis.observer = observer;\n\t}\n\n\t@Override\n\tpublic void onRefresh() {\n\t\tthis.invokeConsumer();\n\t\tAppInfo.notifyUpdate();\n\t}\n\n\tprotected final void invokeConsumer() {\n\t\tConsumer<RefreshableSystemConfig> ob = this.observer;\n\t\tif (ob != null) {\n\t\t\tob.accept(this);\n\t\t}\n\t}\n\n\t@Override\n\tprotected final void onStart() {\n\t\tthis.init();\n\t\tRawLog.setLogger(RawLog.SLF4J_LOG);\n\t\tthis.invokeConsumer();\n\t}\n\n\tprotected abstract void init();\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/AppConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\n\nimport org.yx.log.RawLog;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.IOUtil;\nimport org.yx.util.Task;\n\npublic class AppConfig extends AbstractRefreshableSystemConfig {\n\n\tprotected final String fileName;\n\tprotected final int periodTime;\n\tprotected Map<String, String> map = Collections.emptyMap();\n\tprotected boolean showLog = true;\n\tprivate boolean isFirst = true;\n\tprotected ScheduledFuture<?> future;\n\n\tpublic AppConfig() {\n\t\tthis(System.getProperty(\"sumk.appinfo\", \"app.properties\"));\n\t}\n\n\tpublic AppConfig(String fileName) {\n\t\tthis(fileName, Integer.getInteger(\"sumk.appinfo.period\", 1000 * 30));\n\t}\n\n\tpublic AppConfig(String fileName, int periodTimeMS) {\n\t\tthis.fileName = Objects.requireNonNull(fileName);\n\t\tthis.periodTime = Math.max(periodTimeMS, 1000);\n\t}\n\n\tprivate InputStream openInputStream() throws FileNotFoundException {\n\t\tInputStream in = this.getClass().getClassLoader().getResourceAsStream(fileName);\n\t\tif (in != null) {\n\t\t\treturn in;\n\t\t}\n\t\tFile f = new File(fileName);\n\t\tif (f.exists()) {\n\t\t\treturn new FileInputStream(f);\n\t\t}\n\t\tRawLog.info(LOG_NAME, \"can not found \" + this.fileName);\n\t\treturn null;\n\t}\n\n\tprivate void handle() {\n\t\ttry (InputStream in = openInputStream()) {\n\t\t\tif (in == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbyte[] bs = IOUtil.readAllBytes(in, true);\n\t\t\tMap<String, String> conf = CollectionUtil.fillPropertiesFromText(new HashMap<>(),\n\t\t\t\t\tnew String(bs, StandardCharsets.UTF_8));\n\t\t\tif (conf != null && !conf.equals(this.map)) {\n\t\t\t\tif (this.showLog) {\n\t\t\t\t\tRawLog.info(LOG_NAME, fileName + \" loaded\");\n\t\t\t\t}\n\t\t\t\tthis.map = conf;\n\t\t\t\tif (!isFirst) {\n\t\t\t\t\tthis.onRefresh();\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.isFirst = false;\n\t\t} catch (Exception e) {\n\t\t\tRawLog.error(LOG_NAME, e.getMessage(), e);\n\t\t}\n\t}\n\n\tpublic boolean isShowLog() {\n\t\treturn showLog;\n\t}\n\n\tpublic void setShowLog(boolean showLog) {\n\t\tthis.showLog = showLog;\n\t}\n\n\t@Override\n\tprotected void init() {\n\t\tthis.handle();\n\t\tthis.future = Task.scheduleAtFixedRate(this::handle, this.periodTime, this.periodTime, TimeUnit.MILLISECONDS);\n\t}\n\n\t@Override\n\tpublic Set<String> keys() {\n\t\treturn Collections.unmodifiableSet(this.map.keySet());\n\t}\n\n\t@Override\n\tpublic synchronized void stop() {\n\t\tif (this.future != null) {\n\t\t\tthis.future.cancel(false);\n\t\t}\n\t\tthis.started = false;\n\t}\n\n\t@Override\n\tpublic String get(String key) {\n\t\treturn map.get(key);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn String.valueOf(map);\n\t}\n\n\t@Override\n\tpublic Map<String, String> values() {\n\t\treturn Collections.unmodifiableMap(map);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/AppInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.lang.management.ManagementFactory;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.yx.base.context.AppContext;\nimport org.yx.log.RawLog;\nimport org.yx.util.StringUtil;\n\npublic final class AppInfo {\n\tpublic static final String CLASSPATH_URL_PREFIX = \"classpath:\";\n\tpublic static final String LN = Const.LN;\n\n\tprivate static final List<Consumer<SystemConfig>> observers = new ArrayList<>();\n\tpublic static final Charset UTF8 = StandardCharsets.UTF_8;\n\n\tprivate static SystemConfig info;\n\tprivate static String pid;\n\n\tstatic {\n\t\tinit();\n\t}\n\n\tprivate synchronized static void init() {\n\t\ttry {\n\t\t\tString temp = ManagementFactory.getRuntimeMXBean().getName();\n\t\t\tpid = temp.substring(0, temp.indexOf(\"@\"));\n\t\t} catch (Throwable e) {\n\t\t\tRawLog.error(\"sumk.conf\", e);\n\t\t\tpid = \"UNKNOW\";\n\t\t}\n\t\tif (info != null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tif (refreshConfig()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tRawLog.error(\"sumk.conf\", e);\n\t\t\tAppContext.startFailed();\n\t\t}\n\t\tif (info == null) {\n\t\t\ttry {\n\t\t\t\tsetConfig(ComposedConfig.createSystemConfig(new AppConfig()));\n\t\t\t} catch (Exception e) {\n\t\t\t\tRawLog.error(\"sumk.conf\", e);\n\t\t\t\tAppContext.startFailed();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static String pid() {\n\t\treturn pid;\n\t}\n\n\tprivate static synchronized void setConfig(SystemConfig config) {\n\t\tif (info == null) {\n\t\t\tinfo = config;\n\t\t}\n\t\tconfig.start();\n\t\tLocalhostUtil.setLocalIp(config.get(\"sumk.ip\"));\n\t\tinfo = config;\n\t}\n\n\tstatic synchronized boolean refreshConfig() {\n\t\tSystemConfig config = SystemConfigHolder.config;\n\t\tif (info == config || config == null) {\n\t\t\treturn false;\n\t\t}\n\t\tSystemConfig old = info;\n\t\tsetConfig(config);\n\t\tnotifyUpdate();\n\t\tif (old != null) {\n\t\t\told.stop();\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static String getLocalIp() {\n\t\treturn LocalhostUtil.getLocalIP();\n\t}\n\n\t/**\n\t * @param defaultValue 如果没有设置的话，就返回这个默认值\n\t * @return 当前应用的id\n\t */\n\tpublic static String appId(String defaultValue) {\n\t\treturn get(\"sumk.appId\", defaultValue);\n\t}\n\n\t/**\n\t * @param name name\n\t * @return name不存在的话，返回null\n\t */\n\tpublic static String get(String name) {\n\t\treturn info.get(name);\n\t}\n\n\tpublic static String getLatin(String name) {\n\t\tString v = info.get(name);\n\t\treturn v == null ? v : StringUtil.toLatin(v);\n\t}\n\n\tpublic static String getLatin(String name, String defaultValue) {\n\t\tString v = getLatin(name);\n\t\treturn StringUtil.isEmpty(v) ? defaultValue : v;\n\t}\n\n\tpublic static String get(String name, String defaultValue) {\n\t\tString v = info.get(name);\n\t\tif (v == null || v.isEmpty()) {\n\t\t\treturn defaultValue;\n\t\t}\n\t\treturn v;\n\t}\n\n\tpublic static String get(String first, String second, String defaultValue) {\n\t\tString value = info.get(first);\n\t\tif (value != null && value.length() > 0) {\n\t\t\treturn value;\n\t\t}\n\t\treturn get(second, defaultValue);\n\t}\n\n\tpublic static int getInt(String name, int defaultValue) {\n\t\tString value = info.get(name);\n\t\tif (value == null || value.length() == 0) {\n\t\t\treturn defaultValue;\n\t\t}\n\t\ttry {\n\t\t\treturn Integer.parseInt(value);\n\t\t} catch (Exception e) {\n\t\t\treturn defaultValue;\n\t\t}\n\t}\n\n\tpublic static long getLong(String name, long defaultValue) {\n\t\tString value = info.get(name);\n\t\tif (value == null || value.length() == 0) {\n\t\t\treturn defaultValue;\n\t\t}\n\t\ttry {\n\t\t\treturn Long.parseLong(value);\n\t\t} catch (Exception e) {\n\t\t\treturn defaultValue;\n\t\t}\n\t}\n\n\tpublic static boolean getBoolean(String name, boolean defaultValue) {\n\t\tString value = info.get(name);\n\t\tif (value == null || value.length() == 0) {\n\t\t\treturn defaultValue;\n\t\t}\n\t\tvalue = value.toLowerCase();\n\t\treturn \"1\".equals(value) || \"true\".equals(value);\n\t}\n\n\tpublic static Map<String, String> subMap(String prefix) {\n\t\tif (prefix == null) {\n\t\t\tprefix = \"\";\n\t\t}\n\t\tint len = prefix.length();\n\t\tMap<String, String> map = new HashMap<>();\n\t\tfor (String name : info.keys()) {\n\t\t\tif (name.startsWith(prefix)) {\n\t\t\t\tString v = info.get(name);\n\t\t\t\tif (v != null) {\n\t\t\t\t\tmap.put(name.substring(len), v);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n\tpublic static void addObserver(Consumer<SystemConfig> ob) {\n\t\tsynchronized (observers) {\n\t\t\tif (observers.contains(ob)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tobservers.add(ob);\n\t\t}\n\t\tob.accept(info);\n\t}\n\n\tpublic static void removeObserver(Consumer<SystemConfig> ob) {\n\t\tsynchronized (observers) {\n\t\t\tobservers.remove(ob);\n\t\t}\n\t}\n\n\tpublic static synchronized void notifyUpdate() {\n\t\tList<Consumer<SystemConfig>> consumers;\n\t\tsynchronized (observers) {\n\t\t\tconsumers = new ArrayList<>(observers);\n\t\t}\n\t\tfor (Consumer<SystemConfig> consumer : consumers) {\n\t\t\ttry {\n\t\t\t\tconsumer.accept(info);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tRawLog.error(\"sumk.conf\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static SystemConfig config() {\n\t\treturn info;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/ClassPathXmlFilesLoader.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.io.File;\nimport java.net.URL;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.yx.base.scaner.JarFileUtil;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.util.FileUtil;\nimport org.yx.util.Loader;\n\npublic class ClassPathXmlFilesLoader extends AbstractFilesLoader {\n\n\tpublic ClassPathXmlFilesLoader(String rootUri, String subfix) {\n\t\tsuper(rootUri, subfix);\n\t}\n\n\t@Override\n\tpublic Map<String, byte[]> openResources(String db) throws Exception {\n\t\tEnumeration<URL> urls = Loader.getResources(rootUri);\n\t\tif (urls == null || !urls.hasMoreElements()) {\n\t\t\tif (AppInfo.getBoolean(\"sumk.conf.classpath.force\", false)) {\n\t\t\t\tthrow new SumkException(356453425, \"can not find path \" + rootUri);\n\t\t\t}\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tfinal Map<String, byte[]> map = new HashMap<>();\n\t\tCollection<File> files = new HashSet<>();\n\t\tLogger log = Log.get(\"sumk.conf\");\n\t\tdo {\n\t\t\tURL url = urls.nextElement();\n\t\t\tif (JarFileUtil.URL_PROTOCOL_JAR.equals(url.getProtocol())\n\t\t\t\t\t|| JarFileUtil.URL_PROTOCOL_ZIP.equals(url.getProtocol())) {\n\t\t\t\tMap<String, byte[]> tempMap = subfix == null ? JarFileUtil.exactResourcesInJar(url, null)\n\t\t\t\t\t\t: JarFileUtil.exactResourcesInJar(url, node -> node.endsWith(subfix));\n\t\t\t\tif (log.isDebugEnabled()) {\n\t\t\t\t\tlog.debug(\"url:{},size:{}\", url, tempMap == null ? 0 : tempMap.size());\n\t\t\t\t}\n\t\t\t\tif (tempMap != null && tempMap.size() > 0) {\n\t\t\t\t\tmap.putAll(tempMap);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tFile f = new File(url.toURI());\n\t\t\tif (f.exists() && f.isDirectory()) {\n\t\t\t\tFileUtil.listAllSubFiles(files, f);\n\t\t\t}\n\t\t} while (urls.hasMoreElements());\n\n\t\tif (files.size() > 0) {\n\t\t\tList<FileModifyTime> timeList = new ArrayList<>(files.size());\n\t\t\tfor (File f : files) {\n\t\t\t\tString path = f.getAbsolutePath();\n\t\t\t\tif (subfix != null && !path.endsWith(subfix)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmap.put(path, Files.readAllBytes(f.toPath()));\n\t\t\t\ttimeList.add(new FileModifyTime(path, f.lastModified()));\n\t\t\t}\n\t\t\tthis.times = timeList.toArray(new FileModifyTime[timeList.size()]);\n\t\t} else {\n\t\t\tthis.times = null;\n\t\t}\n\n\t\tif (log.isDebugEnabled()) {\n\t\t\tFileModifyTime[] ts = this.times;\n\t\t\tMap<String, Long> timeMap = new HashMap<>();\n\t\t\tif (ts != null && ts.length > 0) {\n\t\t\t\tfor (FileModifyTime t : ts) {\n\t\t\t\t\ttimeMap.put(t.file, t.lastModify);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlog.debug(\"rootUri:{},times size:{},map size:{}\", this.rootUri, timeMap.size(), map.size());\n\t\t\tfor (String p : map.keySet()) {\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\tsb.append(p).append(':').append(map.get(p).length);\n\t\t\t\tLong lastModify = timeMap.get(p);\n\t\t\t\tif (lastModify != null) {\n\t\t\t\t\tsb.append(\" - \").append(lastModify);\n\t\t\t\t}\n\t\t\t\tlog.debug(sb.toString());\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n}"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/ComposedConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\nimport org.yx.base.matcher.Matchers;\nimport org.yx.log.RawLog;\nimport org.yx.util.CollectionUtil;\n\npublic class ComposedConfig implements SystemConfig, Consumer<RefreshableSystemConfig> {\n\n\t/**\n\t * 优先级最高的配置，是不可变的map，且不为null\n\t */\n\tprivate Map<String, String> top;\n\t/**\n\t * 优先级最低的配置，是不可变的map，且不为null\n\t */\n\tprivate Map<String, String> lowest;\n\tprivate final RefreshableSystemConfig config;\n\n\tprivate Map<String, String> composedMap = Collections.emptyMap();\n\n\tpublic ComposedConfig(Map<String, String> top, RefreshableSystemConfig config) {\n\t\tthis(top, config, null);\n\t}\n\n\tpublic ComposedConfig(Map<String, String> top, RefreshableSystemConfig config, Map<String, String> lowest) {\n\t\tthis.top = CollectionUtil.unmodifyMap(top);\n\t\tthis.lowest = CollectionUtil.unmodifyMap(lowest);\n\t\tthis.config = Objects.requireNonNull(config);\n\t\tthis.config.setConsumer(this);\n\t}\n\n\tpublic static ComposedConfig createSystemConfig(RefreshableSystemConfig conf) {\n\t\tPredicate<String> exclude = Matchers.createWildcardMatcher(\n\t\t\t\t\"java.*,sun.*,jdk.*,awt.toolkit,file.encoding,file.encoding.pkg,file.separator,line.separator,os.arch,os.name,os.version,path.separator,user.country,user.dir,user.home,user.language,user.name,user.script,user.timezone,user.variant\",\n\t\t\t\t1);\n\t\tMap<String, String> map = Collections.emptyMap();\n\t\tfor (int i = 0; i < 1000; i++) {\n\t\t\ttry {\n\t\t\t\tMap<String, String> tmp = new HashMap<>();\n\t\t\t\tProperties p = System.getProperties();\n\t\t\t\tfor (Entry<Object, Object> en : p.entrySet()) {\n\t\t\t\t\tObject k = en.getKey();\n\t\t\t\t\tObject v = en.getValue();\n\t\t\t\t\tif (k != null && k.getClass() == String.class && v != null && v.getClass() == String.class) {\n\t\t\t\t\t\tString key = (String) k;\n\t\t\t\t\t\tif (exclude.test(key)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttmp.put(key, (String) v);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmap = tmp;\n\t\t\t} catch (Exception e) {\n\t\t\t\tRawLog.error(\"sumk.conf\", \"iterate system properties error,\" + e);\n\t\t\t}\n\t\t}\n\t\treturn new ComposedConfig(map, conf);\n\t}\n\n\t@Override\n\tpublic void accept(RefreshableSystemConfig t) {\n\t\tMap<String, String> tmp = new HashMap<>(this.lowest);\n\t\ttmp.putAll(t.values());\n\t\ttmp.putAll(this.top);\n\t\tthis.composedMap = tmp;\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tthis.config.start();\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tthis.config.stop();\n\t}\n\n\t@Override\n\tpublic String get(String key) {\n\t\treturn this.composedMap.get(key);\n\t}\n\n\t@Override\n\tpublic Set<String> keys() {\n\t\treturn Collections.unmodifiableSet(this.composedMap.keySet());\n\t}\n\n\tpublic Map<String, String> topConfig() {\n\t\treturn this.top;\n\t}\n\n\tpublic Map<String, String> lowConfig() {\n\t\treturn this.lowest;\n\t}\n\n\tprotected void topConfig(Map<String, String> top) {\n\t\tthis.top = CollectionUtil.unmodifyMap(top);\n\t}\n\n\tprotected void lowConfig(Map<String, String> low) {\n\t\tthis.lowest = CollectionUtil.unmodifyMap(low);\n\t}\n\n\tpublic int size() {\n\t\treturn this.composedMap.size();\n\t}\n\n\tpublic RefreshableSystemConfig innerConfig() {\n\t\treturn this.config;\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/Const.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\npublic final class Const {\n\n\tpublic static final String DEFAULT_DB_NAME = \"sumk\";\n\n\tpublic static final int SUMK_VERSION = 0x421;\n\n\tpublic static String sumkVersion() {\n\t\treturn new StringBuilder(10).append((Const.SUMK_VERSION >> 8) & 0x0F).append('.')\n\t\t\t\t.append((Const.SUMK_VERSION >> 4) & 0x0F).append('.').append(Const.SUMK_VERSION & 0x0F).toString();\n\t}\n\n\tpublic static final String KEY_STORE_PATH = \"sumk.webserver.ssl.keyStore\";\n\n\t/**\n\t * 分号 ;\n\t */\n\tpublic static final String SEMICOLON = \";\";\n\t/**\n\t * 逗号 ,\n\t */\n\tpublic static final String COMMA = \",\";\n\n\tpublic static final String LN = \"\\n\";\n\n\tpublic static final int DEFAULT_TOPLIMIT = 50000;\n\n\t/**\n\t * 这个监听程序不要直接操作数据库\n\t */\n\tpublic static final String LISTENER_DB_MODIFY = \"LISTENER_DB_MODIFY\";\n\t/**\n\t * 这个监听程序不要直接操作数据库\n\t */\n\tpublic static final String LISTENER_DB_QUERY = \"LISTENER_DB_QUERY\";\n\n\t/**\n\t * 提交前监听，这个监听程序可以操作数据库,它的插入事件也可以被LISTENER_DB_MODIFY监听器监听到。<BR>\n\t * 注意：它不能监听TransactionType为AUTO_COMMIT的数据库操作\n\t */\n\tpublic static final String LISTENER_DB_MODIFY_ON_COMMIT = \"LISTENER_DB_MODIFY_ON_COMMIT\";\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/FileModifyTime.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\npublic class FileModifyTime {\n\n\tfinal String file;\n\tlong lastModify;\n\n\tpublic FileModifyTime(String file, long lastModify) {\n\t\tthis.file = file;\n\t\tthis.lastModify = lastModify;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn file + \":\" + lastModify;\n\t}\n\n\tpublic long getLastModify() {\n\t\treturn lastModify;\n\t}\n\n\tpublic void setLastModify(long lastModify) {\n\t\tthis.lastModify = lastModify;\n\t}\n\n\tpublic String getFile() {\n\t\treturn file;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/LocalMultiResourceLoaderSupplier.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.util.Objects;\nimport java.util.function.Supplier;\n\npublic class LocalMultiResourceLoaderSupplier implements Supplier<MultiResourceLoader> {\n\n\tprivate final String rootUri;\n\n\tpublic LocalMultiResourceLoaderSupplier(String uri) {\n\t\tthis.rootUri = Objects.requireNonNull(uri);\n\t}\n\n\t@Override\n\tpublic MultiResourceLoader get() {\n\t\tString xml = \".xml\";\n\t\tif (rootUri.startsWith(AppInfo.CLASSPATH_URL_PREFIX)) {\n\t\t\treturn new ClassPathXmlFilesLoader(rootUri.substring(AppInfo.CLASSPATH_URL_PREFIX.length()), xml);\n\t\t}\n\t\treturn new AbsoluteXmlFilesLoader(rootUri, xml);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/LocalhostUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.net.Inet6Address;\nimport java.net.InetAddress;\nimport java.net.NetworkInterface;\nimport java.net.SocketException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.base.matcher.Matchers;\nimport org.yx.log.RawLog;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic final class LocalhostUtil {\n\n\tprivate static String localIp = null;\n\tprivate static String[] localIps = null;\n\tprivate static Predicate<String> matcher = defaultMatcher();\n\n\tprivate static Predicate<String> defaultMatcher() {\n\n\t\tPredicate<String> m = Matchers.createWildcardMatcher(\"10.*,172.*,16.*,192.*,168.*\", 1);\n\t\tPredicate<String> not1 = Matchers.createWildcardMatcher(\"*.1\", 1).negate();\n\t\treturn m.and(not1);\n\t}\n\n\tprivate static boolean isValid(InetAddress ia) {\n\t\tif (ia instanceof Inet6Address && AppInfo.getBoolean(\"sumk.local.ipv6.disable\", false)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn !ia.isAnyLocalAddress() && !ia.isLoopbackAddress();\n\t}\n\n\tprivate static String getHostAddress(InetAddress ia) {\n\t\tString address = ia.getHostAddress();\n\t\tint index = address.indexOf('%');\n\t\tif (index > 0 && AppInfo.getBoolean(\"sumk.ipv6.pure\", true)) {\n\t\t\taddress = address.substring(0, index);\n\t\t}\n\t\treturn address;\n\t}\n\n\tpublic static synchronized boolean setLocalIp(String ip) {\n\t\tif (ip == null) {\n\t\t\tip = \"\";\n\t\t}\n\t\ttry {\n\t\t\tip = StringUtil.toLatin(ip).trim();\n\t\t\tif (ip.contains(Matchers.WILDCARD) || ip.contains(\",\")) {\n\t\t\t\tmatcher = Matchers.createWildcardMatcher(ip, 1);\n\t\t\t\tresetLocalIp();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tmatcher = defaultMatcher();\n\t\t\tif (ip.length() > 0) {\n\t\t\t\tlocalIp = ip;\n\t\t\t} else {\n\t\t\t\tresetLocalIp();\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (Exception e) {\n\t\t\tRawLog.error(\"sumk.conf\", e.getMessage(), e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic static String getLocalIP() {\n\t\tif (localIp != null) {// localIp不会从非null变为null\n\t\t\treturn localIp;\n\t\t}\n\t\tresetLocalIp();\n\t\treturn localIp;\n\t}\n\n\tprivate synchronized static void resetLocalIp() {\n\t\ttry {\n\t\t\tString ip = getLocalIP0();\n\t\t\tif (ip != null && ip.length() > 0) {\n\t\t\t\tlocalIp = ip;\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tRawLog.error(\"sumk.conf\", e.getMessage(), e);\n\t\t}\n\t\tif (localIp == null) {\n\t\t\tlocalIp = \"0.0.0.0\";\n\t\t}\n\t}\n\n\tprivate static String getLocalIP0() throws Exception {\n\t\tString[] ips = getLocalIps();\n\t\tPredicate<String> matcher = LocalhostUtil.matcher;\n\t\tif (matcher == null) {\n\t\t\tmatcher = BooleanMatcher.TRUE;\n\t\t}\n\t\tfor (String ip : ips) {\n\t\t\tif (matcher.test(ip)) {\n\t\t\t\treturn ip;\n\t\t\t}\n\t\t}\n\t\tif (ips.length > 0) {\n\t\t\tRawLog.warn(\"sumk.conf\", \"没有合适ip，使用第一个ip，列表为:\" + Arrays.toString(ips));\n\t\t\treturn ips[0];\n\t\t}\n\t\tRawLog.warn(\"sumk.conf\", \"找不到任何ip，使用0.0.0.0\");\n\t\treturn \"0.0.0.0\";\n\t}\n\n\tprivate static synchronized String[] getLocalIps() {\n\t\tif (localIps != null) {\n\t\t\treturn localIps;\n\t\t}\n\t\tList<String> ipList = new ArrayList<String>();\n\t\ttry {\n\t\t\tEnumeration<?> e1 = (Enumeration<?>) NetworkInterface.getNetworkInterfaces();\n\t\t\twhile (e1.hasMoreElements()) {\n\t\t\t\tNetworkInterface ni = (NetworkInterface) e1.nextElement();\n\t\t\t\tif (!isUp(ni)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tEnumeration<?> e2 = ni.getInetAddresses();\n\t\t\t\twhile (e2.hasMoreElements()) {\n\t\t\t\t\tInetAddress ia = (InetAddress) e2.nextElement();\n\t\t\t\t\tif (isValid(ia)) {\n\t\t\t\t\t\tipList.add(getHostAddress(ia));\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SocketException e) {\n\t\t\tRawLog.error(\"sumk.conf\", e.getMessage(), e);\n\t\t}\n\t\tif (ipList.isEmpty()) {\n\t\t\treturn new String[0];\n\t\t}\n\t\tlocalIps = ipList.toArray(new String[ipList.size()]);\n\t\treturn localIps;\n\t}\n\n\t/**\n\t * 网卡是否有效\n\t * \n\t * @param ni\n\t * @return\n\t * @throws SocketException\n\t */\n\tprivate static boolean isUp(NetworkInterface ni) throws SocketException {\n\t\treturn (!ni.isVirtual()) && ni.isUp() && (!ni.isLoopback());\n\t}\n\n\tpublic static List<String> getLocalIPList() {\n\t\treturn CollectionUtil.unmodifyList(getLocalIps());\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/MapConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class MapConfig implements SystemConfig {\n\n\tprivate Map<String, String> map = new ConcurrentHashMap<>();\n\n\tpublic static MapConfig create() {\n\t\treturn new MapConfig();\n\t}\n\n\tpublic Map<String, String> map() {\n\t\treturn map;\n\t}\n\n\tpublic MapConfig replace(Map<String, String> config) {\n\t\tmap = Objects.requireNonNull(config);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String get(String key) {\n\t\treturn map.get(key);\n\t}\n\n\t@Override\n\tpublic Set<String> keys() {\n\t\treturn new HashSet<>(map.keySet());\n\t}\n\n\tpublic MapConfig put(String k, String v) {\n\t\tmap.put(k, v);\n\t\treturn this;\n\t}\n\n\tpublic String remove(String key) {\n\t\treturn this.map.remove(key);\n\t}\n\n\tpublic MapConfig putKV(String kv) {\n\t\tmap.put(kv.split(\"=\")[0].trim(), kv.split(\"=\")[1].trim());\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void start() {\n\n\t}\n\n\t@Override\n\tpublic void stop() {\n\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"MapConfig: \" + map;\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/MultiNodeConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport static org.yx.conf.AppInfo.LN;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.yx.util.CollectionUtil;\n\npublic abstract class MultiNodeConfig extends AbstractRefreshableSystemConfig {\n\tprotected Map<String, String> config = Collections.emptyMap();\n\tprivate Function<byte[], Map<String, String>> dataParser = data -> {\n\t\tString s = new String(data, AppInfo.UTF8).trim().replace(\"\\r\\n\", LN).replace(\"\\r\", LN);\n\t\treturn CollectionUtil.fillPropertiesFromText(new HashMap<>(), s);\n\t};\n\n\tpublic Function<byte[], Map<String, String>> getDataParser() {\n\t\treturn dataParser;\n\t}\n\n\tpublic void setDataParser(Function<byte[], Map<String, String>> dataParser) {\n\t\tthis.dataParser = Objects.requireNonNull(dataParser);\n\t}\n\n\tprotected Map<String, String> parse(byte[] data) {\n\t\treturn this.dataParser.apply(data);\n\t}\n\n\t@Override\n\tpublic String get(String key) {\n\t\treturn config.get(key);\n\t}\n\n\t@Override\n\tpublic Set<String> keys() {\n\t\treturn config.keySet();\n\t}\n\n\t@Override\n\tpublic Map<String, String> values() {\n\t\treturn Collections.unmodifiableMap(this.config);\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/MultiResourceLoader.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.util.Map;\nimport java.util.function.Consumer;\n\npublic interface MultiResourceLoader {\n\n\tMap<String, byte[]> openResources(String name) throws Exception;\n\n\tboolean startListen(Consumer<MultiResourceLoader> consumer);\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/RefreshableSystemConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.util.Map;\nimport java.util.function.Consumer;\n\npublic interface RefreshableSystemConfig extends SystemConfig {\n\tvoid setConsumer(Consumer<RefreshableSystemConfig> observer);\n\n\tvoid onRefresh();\n\n\tMap<String, String> values();\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/SimpleBeanUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.yx.log.Log;\nimport org.yx.util.StringUtil;\n\npublic class SimpleBeanUtil {\n\n\tpublic static void setProperty(Object bean, Method m, String value)\n\t\t\tthrows IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tif (value == null) {\n\t\t\tLog.get(\"sumk.bean\").debug(\"{} was ignored because value is null\", m.getName());\n\t\t\treturn;\n\t\t}\n\t\tvalue = value.trim();\n\t\tif (value.isEmpty()) {\n\t\t\tLog.get(\"sumk.bean\").debug(\"{} was ignored because value is empty\", m.getName());\n\t\t\treturn;\n\t\t}\n\t\tClass<?> ptype = m.getParameterTypes()[0];\n\t\tObject v = parseValue(ptype, value);\n\t\tif (v == null) {\n\t\t\tLog.get(\"sumk.bean\").warn(\"{}因为类型({})不支持，被过滤掉\", m.getName(), ptype);\n\t\t\treturn;\n\t\t}\n\t\tm.invoke(bean, v);\n\t}\n\n\tpublic static Object parseValue(Class<?> ptype, String value) {\n\t\tif (ptype == String.class) {\n\t\t\treturn value;\n\t\t}\n\t\tif (ptype == int.class || ptype == Integer.class) {\n\t\t\treturn Integer.valueOf(value);\n\t\t}\n\t\tif (ptype == long.class || ptype == Long.class) {\n\t\t\treturn Long.valueOf(value);\n\t\t}\n\t\tif (ptype == double.class || ptype == Double.class) {\n\t\t\treturn Double.valueOf(value);\n\t\t}\n\t\tif (ptype == boolean.class || ptype == Boolean.class) {\n\t\t\tif (\"1\".equals(value) || \"true\".equalsIgnoreCase(value)) {\n\t\t\t\treturn Boolean.TRUE;\n\t\t\t}\n\t\t\treturn Boolean.FALSE;\n\t\t}\n\n\t\tif (ptype == short.class || ptype == Short.class) {\n\t\t\treturn Short.valueOf(value);\n\t\t}\n\t\tif (ptype == byte.class || ptype == Byte.class) {\n\t\t\treturn Byte.valueOf(value);\n\t\t}\n\t\tif (ptype == float.class || ptype == Float.class) {\n\t\t\treturn Float.valueOf(value);\n\t\t}\n\n\t\tif (ptype == char.class || ptype == Character.class) {\n\t\t\tif (value.length() > 0) {\n\t\t\t\treturn value.charAt(0);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\n\t}\n\n\tpublic static void copyProperties(Object bean, Map<String, String> map) throws Exception {\n\t\tSet<String> set = map.keySet();\n\t\tMethod[] ms = bean.getClass().getMethods();\n\t\tfor (String key : set) {\n\t\t\tMethod m = getMethod(ms, key);\n\t\t\tif (m == null) {\n\t\t\t\tLog.get(\"sumk.bean\").warn(\"{}在{}中不存在\", key, bean.getClass().getSimpleName());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsetProperty(bean, m, map.get(key));\n\t\t}\n\t}\n\n\tprivate static Method getMethod(Method[] ms, String key) {\n\t\tString methodName = \"set\" + StringUtil.capitalize(key);\n\t\tfor (Method m : ms) {\n\t\t\tif (m.getName().equals(methodName) && m.getParameterCount() == 1) {\n\t\t\t\treturn m;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/SystemConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.util.Set;\n\nimport org.yx.base.Lifecycle;\n\npublic interface SystemConfig extends Lifecycle {\n\tString get(String key);\n\n\tSet<String> keys();\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/SystemConfigHolder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.util.Objects;\n\npublic class SystemConfigHolder {\n\tstatic SystemConfig config;\n\n\t/**\n\t * 通过外部方式注入，这种方式不一定会调用init方法\n\t * \n\t * @param config 外部注入\n\t * @return true表示设置被更新，如果config跟原来是同一个对象，也会返回false\n\t */\n\tpublic static synchronized boolean setSystemConfig(SystemConfig config) {\n\t\tSystemConfigHolder.config = Objects.requireNonNull(config);\n\t\treturn AppInfo.refreshConfig();\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/conf/UrlSystemConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.conf;\n\nimport java.io.InputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\n\nimport org.yx.exception.SumkException;\nimport org.yx.log.RawLog;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.IOUtil;\nimport org.yx.util.Task;\n\npublic class UrlSystemConfig extends MultiNodeConfig {\n\n\tprotected final List<URL> urls;\n\n\tprotected long period = 1000L * 60 * 5;\n\tprotected int timeout = 5000;\n\tprotected int readTimeout = 5000;\n\tprotected String method = \"GET\";\n\n\tprivate Future<?> future;\n\n\tpublic UrlSystemConfig(List<URL> urls) {\n\t\tif (urls == null || urls.isEmpty()) {\n\t\t\tthrow new SumkException(345212465, \"url config的地址为空\");\n\t\t}\n\t\tthis.urls = CollectionUtil.unmodifyList(urls);\n\t}\n\n\tpublic final synchronized void stop() {\n\t\tthis.started = false;\n\t\tthis.onStop();\n\t}\n\n\tprotected void onStop() {\n\t\tFuture<?> f = this.future;\n\t\tif (f != null) {\n\t\t\tf.cancel(true);\n\t\t\tthis.future = null;\n\t\t}\n\t}\n\n\t/**\n\t * 初始化\n\t */\n\t@Override\n\tpublic void init() {\n\t\tif (this.future != null) {\n\t\t\treturn;\n\t\t}\n\t\tthis.future = Task.scheduleAtFixedRate(this::handle, this.period, this.period, TimeUnit.MILLISECONDS);\n\t}\n\n\tprotected void handle() {\n\t\tMap<String, String> map = new HashMap<>();\n\t\tList<URL> list = new ArrayList<>(urls);\n\t\tCollections.reverse(list);\n\t\tfor (URL url : list) {\n\t\t\tbyte[] data = this.extractData(url);\n\t\t\tif (data == null) {\n\t\t\t\tRawLog.error(LOG_NAME, \"data on [\" + url + \"] is null\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (data.length > 0) {\n\t\t\t\tmap.putAll(this.parse(data));\n\t\t\t}\n\t\t}\n\t\tif (Objects.equals(this.config, map)) {\n\t\t\treturn;\n\t\t}\n\t\tRawLog.info(LOG_NAME, \"data changed\");\n\t\tthis.config = map;\n\t\tthis.onRefresh();\n\t}\n\n\tprotected byte[] extractData(URL url) {\n\t\tHttpURLConnection conn = null;\n\t\ttry {\n\n\t\t\tconn = (HttpURLConnection) url.openConnection();\n\t\t\tconn.setConnectTimeout(timeout);\n\t\t\tconn.setRequestMethod(method);\n\t\t\tconn.setReadTimeout(readTimeout);\n\t\t\tconn.setDoOutput(true);\n\t\t\tconn.connect();\n\t\t\tif (conn.getResponseCode() != 200) {\n\t\t\t\tRawLog.error(LOG_NAME, url + \"返回的状态码是\" + conn.getResponseCode());\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tInputStream in = conn.getInputStream();\n\t\t\treturn IOUtil.readAllBytes(in, true);\n\t\t} catch (Exception e) {\n\t\t\tRawLog.error(LOG_NAME, e);\n\t\t\treturn null;\n\t\t} finally {\n\t\t\tif (conn != null) {\n\t\t\t\ttry {\n\t\t\t\t\tconn.disconnect();\n\t\t\t\t} catch (Exception e2) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic int getTimeout() {\n\t\treturn timeout;\n\t}\n\n\tpublic void setTimeout(int timeout) {\n\t\tthis.timeout = timeout;\n\t}\n\n\tpublic int getReadTimeout() {\n\t\treturn readTimeout;\n\t}\n\n\tpublic void setReadTimeout(int readTimeout) {\n\t\tthis.readTimeout = readTimeout;\n\t}\n\n\tpublic String getMethod() {\n\t\treturn method;\n\t}\n\n\tpublic void setMethod(String method) {\n\t\tthis.method = method;\n\t}\n\n\tpublic long getPeriod() {\n\t\treturn period;\n\t}\n\n\tpublic void setPeriod(long period) {\n\t\tif (this.future != null) {\n\t\t\tthrow new SumkException(62654622, \"启动后不允许修改刷新间隔\");\n\t\t}\n\t\tthis.period = period;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/exception/BizException.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.exception;\n\nimport org.yx.conf.AppInfo;\n\n/**\n * 它的code最好要大于0，并且避开3位数，因为内置的错误码都是3位数\n */\npublic class BizException extends CodeException {\n\n\tprivate static final long serialVersionUID = 453453454L;\n\n\tpublic BizException(int code, String msg) {\n\t\tsuper(String.valueOf(code), msg);\n\t}\n\n\tpublic BizException(String code, String msg) {\n\t\tsuper(code, msg);\n\t}\n\n\tpublic static BizException create(int code, String msg) {\n\t\treturn new BizException(code, msg);\n\t}\n\n\tpublic static void throwException(int code, String msg) throws BizException {\n\t\tthrow new BizException(code, msg);\n\t}\n\n\tpublic static BizException create(String code, String msg) {\n\t\treturn new BizException(code, msg);\n\t}\n\n\tpublic static void throwException(String code, String msg) throws BizException {\n\t\tthrow new BizException(code, msg);\n\t}\n\n\t@Override\n\tpublic Throwable fillInStackTrace() {\n\t\tif (AppInfo.getBoolean(\"sumk.bizexception.fullstack\", true)) {\n\t\t\treturn super.fillInStackTrace();\n\t\t}\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/exception/CodeException.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.exception;\n\nimport java.util.Objects;\n\npublic abstract class CodeException extends RuntimeException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprotected final String code;\n\n\tpublic CodeException(String code, String msg) {\n\t\tsuper(msg);\n\t\tthis.code = Objects.requireNonNull(code);\n\t}\n\n\tpublic CodeException(String code, String msg, Throwable exception) {\n\t\tsuper(msg, exception);\n\t\tthis.code = Objects.requireNonNull(code);\n\t}\n\n\tpublic String getCode() {\n\t\treturn code;\n\t}\n\n\tpublic boolean isSameCode(String expect) {\n\t\treturn this.code.equals(expect);\n\t}\n\n\t@Override\n\tpublic String getLocalizedMessage() {\n\t\treturn new StringBuilder().append(this.getMessage()).append(\" [\").append(code).append(']').toString();\n\t}\n\n\t@Override\n\tpublic final String toString() {\n\t\treturn new StringBuilder(this.getClass().getSimpleName()).append(\": \").append(this.getLocalizedMessage())\n\t\t\t\t.toString();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/exception/SimpleSumkException.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.exception;\n\npublic final class SimpleSumkException extends SumkException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic SimpleSumkException(int code, String msg) {\n\t\tsuper(code, msg);\n\t}\n\n\t@Override\n\tpublic Throwable fillInStackTrace() {\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/exception/SoaException.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.exception;\n\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.util.ExceptionUtil;\n\npublic final class SoaException extends CodeException {\n\n\tprivate static final long serialVersionUID = 453453343454L;\n\n\tprivate String detailError;\n\tprivate String exceptionClz;\n\tprivate boolean bizException;\n\n\tpublic String getDetailError() {\n\t\treturn this.detailError;\n\t}\n\n\tpublic String getExceptionClz() {\n\t\treturn exceptionClz;\n\t}\n\n\tpublic static SoaException create(String code, String msg, Throwable cause) {\n\t\tString detailError = getException(cause);\n\t\tSoaException ex;\n\t\tif (cause instanceof BizException) {\n\t\t\tBizException bizEx = (BizException) cause;\n\t\t\tex = new SoaException(bizEx.getCode(), bizEx.getMessage(), detailError);\n\t\t\tex.bizException = true;\n\t\t} else {\n\t\t\tex = new SoaException(code, msg, detailError);\n\t\t}\n\t\tex.exceptionClz = cause == null ? null : cause.getClass().getName();\n\t\treturn ex;\n\t}\n\n\tpublic SoaException(String code, String msg, String detail) {\n\t\tsuper(code, msg);\n\t\tthis.exceptionClz = null;\n\t\tthis.detailError = detail;\n\t}\n\n\tprivate static String getException(Throwable e) {\n\t\tif (e == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (AppInfo.getBoolean(\"sumk.rpc.detailError\", false)) {\n\t\t\tStringBuilder sb = new StringBuilder(64);\n\t\t\tExceptionUtil.printStackTrace(sb, e);\n\t\t\tif (sb.length() >= 4000) {\n\t\t\t\treturn sb.substring(0, 4000);\n\t\t\t}\n\t\t\treturn sb.toString();\n\t\t}\n\t\treturn e.getMessage();\n\t}\n\n\tprivate boolean printRawStackTrace() {\n\t\treturn AppInfo.getBoolean(\"sumk.rpc.printRawStackTrace\", false);\n\t}\n\n\t@Override\n\tpublic Throwable fillInStackTrace() {\n\t\tif (printRawStackTrace()) {\n\t\t\tsuper.fillInStackTrace();\n\t\t}\n\t\treturn this;\n\t}\n\n\tprivate String buildStackTraceMessage() {\n\t\tStringBuilder sb = new StringBuilder().append(this.toString());\n\t\tif (this.exceptionClz != null && this.exceptionClz.length() > 0) {\n\t\t\tsb.append(\"\\n\\tcause by \" + this.exceptionClz);\n\t\t\tif (this.detailError != null && this.detailError.length() > 0) {\n\t\t\t\tsb.append(\":\").append(this.detailError);\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic void printStackTrace(PrintStream s) {\n\t\tif (printRawStackTrace()) {\n\t\t\tsuper.printStackTrace(s);\n\t\t\treturn;\n\t\t}\n\n\t\ts.println(this.buildStackTraceMessage());\n\t}\n\n\t@Override\n\tpublic void printStackTrace(PrintWriter s) {\n\t\tif (printRawStackTrace()) {\n\t\t\tsuper.printStackTrace(s);\n\t\t\treturn;\n\t\t}\n\t\ts.println(this.buildStackTraceMessage());\n\t}\n\n\tpublic boolean isBizException() {\n\t\treturn bizException;\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/exception/SumkException.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.exception;\n\n/**\n * 这个异常表示是执行sumk框架中的某个方法抛出的异常,它的code最大为0<BR>\n * 框架内部使用，业务异常请用BizException\n *\n */\npublic class SumkException extends CodeException {\n\n\tprivate static final long serialVersionUID = 3453246435546L;\n\n\tpublic SumkException(int code, String msg) {\n\t\tsuper(stringCode(code), msg);\n\t}\n\n\tpublic SumkException(int code, String msg, Throwable exception) {\n\t\tsuper(stringCode(code), msg, exception);\n\t}\n\n\tprivate static String stringCode(int code) {\n\t\treturn String.valueOf(code);\n\t}\n\n\tpublic static SumkException wrap(Throwable e) {\n\t\tif (e instanceof SumkException) {\n\t\t\tthrow (SumkException) e;\n\t\t}\n\t\treturn new SumkException(34534565, e.getMessage(), e);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/exception/SumkExceptionCode.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.exception;\n\n/**\n * 这里的code是不可变的，可以用它对SumkException中的code做判断。 它们都是-91275XXXX格式\n */\npublic interface SumkExceptionCode {\n\t/**\n\t * 存在多个bean\n\t */\n\tint TOO_MANY_BEAN = 912753951;\n\n\t/**\n\t * 数据库连接已经关闭\n\t */\n\tint DB_CONNECTION_CLOSED = 912753820;\n\n\t/**\n\t * 数据库queryOne()的时候，返回的结果不止一条\n\t */\n\tint DB_TOO_MANY_RESULTS = 912753811;\n\n\t/**\n\t * redis连接异常\n\t */\n\tint REDIS_DIS_CONNECTION = 912753701;\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/CodeLineMarker.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\nimport java.util.Iterator;\nimport java.util.NoSuchElementException;\n\nimport org.slf4j.Marker;\n\npublic class CodeLineMarker implements Marker {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate final String packageName;\n\n\tpublic CodeLineMarker(String packageName) {\n\t\tthis.packageName = packageName;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn packageName;\n\t}\n\n\t@Override\n\tpublic void add(Marker reference) {\n\n\t}\n\n\t@Override\n\tpublic boolean remove(Marker reference) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean hasChildren() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean hasReferences() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Iterator<Marker> iterator() {\n\t\treturn new Iterator<Marker>() {\n\n\t\t\t@Override\n\t\t\tpublic boolean hasNext() {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Marker next() {\n\t\t\t\tthrow new NoSuchElementException();\n\t\t\t}\n\n\t\t};\n\t}\n\n\t@Override\n\tpublic boolean contains(Marker other) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean contains(String name) {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/ConsoleLog.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\nimport org.slf4j.Marker;\nimport org.yx.util.SumkDate;\n\npublic class ConsoleLog extends SumkLogger {\n\n\tprivate static final Loggers loggers = Loggers.create(\"ConsoleLog\");\n\n\tpublic static final SumkLogger defaultLog = ConsoleLog.get(\"sumk.log\");\n\n\tpublic static SumkLogger get(String name) {\n\t\tSumkLogger log = loggers.get(name);\n\t\tif (log != null) {\n\t\t\treturn log;\n\t\t}\n\t\tlog = new ConsoleLog(name);\n\t\tSumkLogger oldInstance = loggers.putIfAbsent(name, log);\n\t\treturn oldInstance == null ? log : oldInstance;\n\t}\n\n\tprivate ConsoleLog(String module) {\n\t\tsuper(module);\n\t}\n\n\t@Override\n\tprotected void output(Marker marker, LogLevel methodLevel, String format, Object... arguments) {\n\t\tthis.show(methodLevel, format, arguments);\n\t}\n\n\t@Override\n\tprotected void output(Marker marker, LogLevel methodLevel, String msg, Throwable e) {\n\t\tStringBuilder sb = new StringBuilder(128);\n\t\tsb.append(currentTime()).append(\" [\").append(Thread.currentThread().getName()).append(\"] \").append(methodLevel)\n\t\t\t\t.append(\" \").append(LogKits.shorterPrefix(name, 40)).append(\" - \").append(msg).append(\"\\n\");\n\t\tSystem.err.print(sb.toString());\n\t\te.printStackTrace();\n\t}\n\n\tprivate void show(LogLevel level, String msg, Object... args) {\n\t\tmsg = LogKits.buildMessage(msg, args);\n\t\tStringBuilder sb = new StringBuilder(128);\n\t\tsb.append(currentTime()).append(\" [\");\n\t\tsb.append(Thread.currentThread().getName()).append(\"] \").append(level).append(\" \")\n\t\t\t\t.append(LogKits.shorterPrefix(name, 40)).append(\" - \").append(msg);\n\t\tSystem.out.println(sb.toString());\n\t}\n\n\tprivate StringBuilder currentTime() {\n\t\tSumkDate d = SumkDate.now();\n\t\tStringBuilder sb = new StringBuilder(26);\n\t\tsb.append(d.to_yyyy_MM_dd()).append('T').append(d.to_HH_mm_ss_SSS());\n\t\treturn sb;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/DelegateLogger.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\nimport java.util.Objects;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.Marker;\n\npublic final class DelegateLogger implements Logger {\n\n\tprivate static int count = 0;\n\n\tprivate static void incrCount() {\n\t\tcount++;\n\t\tif (count < 0) {\n\t\t\tcount = 0;\n\t\t}\n\t}\n\n\tprivate Logger delegate;\n\n\tprivate DelegateLogger(String name) {\n\t\tthis.delegate = ConsoleLog.get(name);\n\t}\n\n\tpublic static DelegateLogger get(String name) {\n\t\treturn new DelegateLogger(name);\n\t}\n\n\tpublic Logger delegate() {\n\t\tif (delegate.getClass() != ConsoleLog.class) {\n\t\t\treturn delegate;\n\t\t}\n\t\tincrCount();\n\t\tif (count % 20 != 10) {\n\t\t\treturn delegate;\n\t\t}\n\t\ttry {\n\t\t\tLogger slfLog = LoggerFactory.getLogger(delegate.getName());\n\t\t\tif (slfLog == null) {\n\t\t\t\treturn delegate;\n\t\t\t}\n\t\t\tif (slfLog instanceof ConsoleLog || slfLog instanceof DelegateLogger) {\n\t\t\t\treturn delegate;\n\t\t\t}\n\t\t\tif (Log.isNOPLogger(slfLog)) {\n\t\t\t\treturn delegate;\n\t\t\t}\n\t\t\tdelegate = slfLog;\n\t\t\tConsoleLog.defaultLog.info(\"{} change to {}\", delegate.getName(), delegate.getClass().getSimpleName());\n\t\t} catch (Throwable e) {\n\t\t\tConsoleLog.defaultLog.warn(\"can not change to slf4j. throwable {}:{}\", e.getClass().getName(),\n\t\t\t\t\te.getMessage());\n\t\t}\n\n\t\treturn delegate;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn delegate().getName();\n\t}\n\n\t@Override\n\tpublic boolean isTraceEnabled() {\n\t\treturn delegate().isTraceEnabled();\n\t}\n\n\t@Override\n\tpublic void trace(String msg) {\n\t\tdelegate().trace(msg);\n\t}\n\n\t@Override\n\tpublic void trace(String format, Object arg) {\n\t\tdelegate().trace(format, arg);\n\t}\n\n\t@Override\n\tpublic void trace(String format, Object arg1, Object arg2) {\n\t\tdelegate().trace(format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void trace(String format, Object... arguments) {\n\t\tdelegate().trace(format, arguments);\n\t}\n\n\t@Override\n\tpublic void trace(String msg, Throwable t) {\n\t\tdelegate().trace(msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isTraceEnabled(Marker marker) {\n\t\treturn delegate().isTraceEnabled(marker);\n\t}\n\n\t@Override\n\tpublic void trace(Marker marker, String msg) {\n\t\tdelegate().trace(marker, msg);\n\t}\n\n\t@Override\n\tpublic void trace(Marker marker, String format, Object arg) {\n\t\tdelegate().trace(marker, format, arg);\n\t}\n\n\t@Override\n\tpublic void trace(Marker marker, String format, Object arg1, Object arg2) {\n\t\tdelegate().trace(marker, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void trace(Marker marker, String format, Object... arguments) {\n\t\tdelegate().trace(marker, format, arguments);\n\t}\n\n\t@Override\n\tpublic void trace(Marker marker, String msg, Throwable t) {\n\t\tdelegate().trace(marker, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isDebugEnabled() {\n\t\treturn delegate().isDebugEnabled();\n\t}\n\n\t@Override\n\tpublic void debug(String msg) {\n\t\tdelegate().debug(msg);\n\t}\n\n\t@Override\n\tpublic void debug(String format, Object arg) {\n\t\tdelegate().debug(format, arg);\n\t}\n\n\t@Override\n\tpublic void debug(String format, Object arg1, Object arg2) {\n\t\tdelegate().debug(format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void debug(String format, Object... arguments) {\n\t\tdelegate().debug(format, arguments);\n\t}\n\n\t@Override\n\tpublic void debug(String msg, Throwable t) {\n\t\tdelegate().debug(msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isDebugEnabled(Marker marker) {\n\t\treturn delegate().isDebugEnabled(marker);\n\t}\n\n\t@Override\n\tpublic void debug(Marker marker, String msg) {\n\t\tdelegate().debug(marker, msg);\n\t}\n\n\t@Override\n\tpublic void debug(Marker marker, String format, Object arg) {\n\t\tdelegate().debug(marker, format, arg);\n\t}\n\n\t@Override\n\tpublic void debug(Marker marker, String format, Object arg1, Object arg2) {\n\t\tdelegate().debug(marker, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void debug(Marker marker, String format, Object... arguments) {\n\t\tdelegate().debug(marker, format, arguments);\n\t}\n\n\t@Override\n\tpublic void debug(Marker marker, String msg, Throwable t) {\n\t\tdelegate().debug(marker, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isInfoEnabled() {\n\t\treturn delegate().isInfoEnabled();\n\t}\n\n\t@Override\n\tpublic void info(String msg) {\n\t\tdelegate().info(msg);\n\t}\n\n\t@Override\n\tpublic void info(String format, Object arg) {\n\t\tdelegate().info(format, arg);\n\t}\n\n\t@Override\n\tpublic void info(String format, Object arg1, Object arg2) {\n\t\tdelegate().info(format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void info(String format, Object... arguments) {\n\t\tdelegate().info(format, arguments);\n\t}\n\n\t@Override\n\tpublic void info(String msg, Throwable t) {\n\t\tdelegate().info(msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isInfoEnabled(Marker marker) {\n\t\treturn delegate().isInfoEnabled(marker);\n\t}\n\n\t@Override\n\tpublic void info(Marker marker, String msg) {\n\t\tdelegate().info(marker, msg);\n\t}\n\n\t@Override\n\tpublic void info(Marker marker, String format, Object arg) {\n\t\tdelegate().info(marker, format, arg);\n\t}\n\n\t@Override\n\tpublic void info(Marker marker, String format, Object arg1, Object arg2) {\n\t\tdelegate().info(marker, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void info(Marker marker, String format, Object... arguments) {\n\t\tdelegate().info(marker, format, arguments);\n\t}\n\n\t@Override\n\tpublic void info(Marker marker, String msg, Throwable t) {\n\t\tdelegate().info(marker, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isWarnEnabled() {\n\t\treturn delegate().isWarnEnabled();\n\t}\n\n\t@Override\n\tpublic void warn(String msg) {\n\t\tdelegate().warn(msg);\n\t}\n\n\t@Override\n\tpublic void warn(String format, Object arg) {\n\t\tdelegate().warn(format, arg);\n\t}\n\n\t@Override\n\tpublic void warn(String format, Object arg1, Object arg2) {\n\t\tdelegate().warn(format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void warn(String format, Object... arguments) {\n\t\tdelegate().warn(format, arguments);\n\t}\n\n\t@Override\n\tpublic void warn(String msg, Throwable t) {\n\t\tdelegate().warn(msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isWarnEnabled(Marker marker) {\n\t\treturn delegate().isWarnEnabled(marker);\n\t}\n\n\t@Override\n\tpublic void warn(Marker marker, String msg) {\n\t\tdelegate().warn(marker, msg);\n\t}\n\n\t@Override\n\tpublic void warn(Marker marker, String format, Object arg) {\n\t\tdelegate().warn(marker, format, arg);\n\t}\n\n\t@Override\n\tpublic void warn(Marker marker, String format, Object arg1, Object arg2) {\n\t\tdelegate().warn(marker, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void warn(Marker marker, String format, Object... arguments) {\n\t\tdelegate().warn(marker, format, arguments);\n\t}\n\n\t@Override\n\tpublic void warn(Marker marker, String msg, Throwable t) {\n\t\tdelegate().warn(marker, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isErrorEnabled() {\n\t\treturn delegate().isErrorEnabled();\n\t}\n\n\t@Override\n\tpublic void error(String msg) {\n\t\tdelegate().error(msg);\n\t}\n\n\t@Override\n\tpublic void error(String format, Object arg) {\n\t\tdelegate().error(format, arg);\n\t}\n\n\t@Override\n\tpublic void error(String format, Object arg1, Object arg2) {\n\t\tdelegate().error(format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void error(String format, Object... arguments) {\n\t\tdelegate().error(format, arguments);\n\t}\n\n\t@Override\n\tpublic void error(String msg, Throwable t) {\n\t\tdelegate().error(msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isErrorEnabled(Marker marker) {\n\t\treturn delegate().isErrorEnabled(marker);\n\t}\n\n\t@Override\n\tpublic void error(Marker marker, String msg) {\n\t\tdelegate().error(marker, msg);\n\t}\n\n\t@Override\n\tpublic void error(Marker marker, String format, Object arg) {\n\t\tdelegate().error(marker, format, arg);\n\t}\n\n\t@Override\n\tpublic void error(Marker marker, String format, Object arg1, Object arg2) {\n\t\tdelegate().error(marker, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void error(Marker marker, String format, Object... arguments) {\n\t\tdelegate().error(marker, format, arguments);\n\t}\n\n\t@Override\n\tpublic void error(Marker marker, String msg, Throwable t) {\n\t\tdelegate().error(marker, msg, t);\n\t}\n\n\tpublic void setDelegate(Logger delegate) {\n\t\tthis.delegate = Objects.requireNonNull(delegate);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/Log.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic final class Log {\n\tprivate static final String SUMKBOX = \".sumkbox.\";\n\n\tprivate Log() {\n\t}\n\n\tpublic static boolean isTraceEnable(String module) {\n\t\treturn get(module).isTraceEnabled();\n\t}\n\n\tpublic static Logger get(Class<?> clz) {\n\t\tString name = clz.getName();\n\t\tif (name.contains(SUMKBOX)) {\n\t\t\tint index = name.lastIndexOf(SUMKBOX);\n\t\t\tname = String.join(\".\", name.substring(0, index), name.substring(index + SUMKBOX.length()));\n\t\t}\n\t\treturn get(name);\n\t}\n\n\tpublic static Logger get(String module) {\n\t\tif (module == null) {\n\t\t\tmodule = \"\";\n\t\t}\n\t\tLogger logger = LoggerFactory.getLogger(module);\n\t\tif (isNOPLogger(logger)) {\n\t\t\treturn DelegateLogger.get(module);\n\t\t}\n\t\treturn logger;\n\t}\n\n\tpublic static boolean isNOPLogger(Logger logger) {\n\t\treturn \"NOPLogger\".equals(logger.getClass().getSimpleName());\n\t}\n\n\tpublic static void printStack(String module, Throwable e) {\n\t\tget(module).error(e.getLocalizedMessage(), e);\n\t}\n\n\tpublic static boolean isON(Logger log) {\n\n\t\treturn log.isTraceEnabled() && log instanceof SumkLogger && ((SumkLogger) log).isON();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/LogKits.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\nimport org.slf4j.spi.LocationAwareLogger;\n\npublic final class LogKits {\n\tprivate static final String DELIM_STR = \"{}\";\n\n\tpublic static String shorterPrefix(String name, int maxLogNameLength) {\n\t\tif (maxLogNameLength < 5 || name == null || name.length() <= maxLogNameLength) {\n\t\t\treturn name;\n\t\t}\n\t\treturn \"..\".concat(name.substring(name.length() - maxLogNameLength + 2));\n\t}\n\n\tpublic static String shorterSubfix(String text, int maxLogNameLength) {\n\t\tif (maxLogNameLength < 5 || text == null || text.length() <= maxLogNameLength) {\n\t\t\treturn text;\n\t\t}\n\t\treturn text.substring(0, maxLogNameLength - 2).concat(\"..\");\n\t}\n\n\tpublic static String buildMessage(String msg, Object... args) {\n\t\tif (msg == null || args == null || args.length == 0 || !msg.contains(DELIM_STR)) {\n\t\t\treturn msg;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(msg.length() + 50);\n\n\t\tint argIndex = 0;\n\t\tint start = 0;\n\t\tint index = 0;\n\t\twhile (argIndex < args.length && (index = msg.indexOf(DELIM_STR, start)) >= 0) {\n\t\t\tint escapeCount = 0;\n\t\t\tfor (int i = index - 1; i >= start; i--) {\n\t\t\t\tif (msg.charAt(i) != '\\\\') {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tescapeCount++;\n\t\t\t}\n\t\t\tsb.append(msg.substring(start, index));\n\t\t\tif (escapeCount % 2 == 0) {\n\t\t\t\tsb.append(String.valueOf(args[argIndex]));\n\t\t\t\targIndex++;\n\t\t\t} else {\n\t\t\t\tsb.append(DELIM_STR);\n\t\t\t}\n\t\t\tstart = index + 2;\n\t\t}\n\t\tif (start < msg.length()) {\n\t\t\tsb.append(msg.substring(start));\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static LogLevel fromSlf4jLocationAwareLoggerInt(int logger_int) {\n\t\tif (logger_int >= LocationAwareLogger.ERROR_INT) {\n\t\t\treturn LogLevel.ERROR;\n\t\t}\n\t\tif (logger_int >= LocationAwareLogger.WARN_INT) {\n\t\t\treturn LogLevel.WARN;\n\t\t}\n\t\tif (logger_int >= LocationAwareLogger.INFO_INT) {\n\t\t\treturn LogLevel.INFO;\n\t\t}\n\t\tif (logger_int >= LocationAwareLogger.DEBUG_INT) {\n\t\t\treturn LogLevel.DEBUG;\n\t\t}\n\t\treturn LogLevel.TRACE;\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/LogLevel.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\npublic enum LogLevel {\n\tON, TRACE, DEBUG, INFO, WARN, ERROR, OFF;\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/LogSettings.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\nimport org.yx.conf.AppInfo;\n\npublic final class LogSettings {\n\tprivate static final int DEFAULT_MAX_BODY_LENGTH = 1500;\n\tprivate static final int DEFAULT_MAX_LOG_NAME_LENGTH = 32;\n\n\tprivate static boolean console;\n\tprivate static boolean showAttach;\n\tprivate static int maxBodyLength = DEFAULT_MAX_BODY_LENGTH;\n\tprivate static int maxLogNameLength = DEFAULT_MAX_LOG_NAME_LENGTH;\n\n\tpublic static void updateSettings() {\n\t\tconsole = AppInfo.getBoolean(\"sumk.log.console\", false);\n\t\tshowAttach = AppInfo.getBoolean(\"sumk.log.attach.show\", true);\n\t\tmaxBodyLength = AppInfo.getInt(\"sumk.log.body.maxlength\", DEFAULT_MAX_BODY_LENGTH);\n\t\tmaxLogNameLength = AppInfo.getInt(\"sumk.log.maxLogNameLength\", DEFAULT_MAX_LOG_NAME_LENGTH);\n\t}\n\n\tpublic static boolean showAttach() {\n\t\treturn showAttach;\n\t}\n\n\tpublic static boolean consoleEnable() {\n\t\treturn console;\n\t}\n\n\tpublic static int maxBodyLength() {\n\t\treturn maxBodyLength;\n\t}\n\n\tpublic static int maxLogNameLength() {\n\t\treturn maxLogNameLength;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/Loggers.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.function.Consumer;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.conf.SystemConfig;\nimport org.yx.util.StringUtil;\n\npublic final class Loggers {\n\tprivate static final String ROOT = \"\";\n\tprivate static LogLevel DEFAULT_LEVEL = LogLevel.INFO;\n\n\tprivate static ConcurrentMap<String, LogLevel> _levelMap = new ConcurrentHashMap<>();\n\n\tprivate ConcurrentMap<String, SumkLogger> map = new ConcurrentHashMap<>();\n\tprivate final String name;\n\n\tprivate Loggers(String name) {\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Loggers [name=\" + name + \"]\";\n\t}\n\n\tprivate static final Consumer<SystemConfig> observer = info -> {\n\t\ttry {\n\t\t\tMap<String, LogLevel> newLevels = new HashMap<>();\n\t\t\tString temp = AppInfo.getLatin(\"sumk.log.level\", null);\n\t\t\tif (temp == null) {\n\t\t\t\tLoggers.resetLevel(newLevels);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tList<String> levelStrs = StringUtil.splitAndTrim(temp, Const.COMMA, Const.SEMICOLON, \"\\r\", Const.LN);\n\t\t\tfor (String levelStr : levelStrs) {\n\t\t\t\tString[] levelNamePair = levelStr.split(\":\");\n\t\t\t\tswitch (levelNamePair.length) {\n\t\t\t\tcase 1:\n\t\t\t\t\tString level = levelNamePair[0].trim().toUpperCase();\n\t\t\t\t\tif (level.contains(\".\")) {\n\t\t\t\t\t\tSystem.err.println(\"[\" + levelStr + \"] is not valid name:level format\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tnewLevels.put(ROOT, LogLevel.valueOf(level));\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tnewLevels.put(levelNamePair[0].trim(), LogLevel.valueOf(levelNamePair[1].trim().toUpperCase()));\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tSystem.err.println(levelStr + \" is not valid [name:level] format\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tLoggers.resetLevel(newLevels);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t};\n\tstatic {\n\t\tAppInfo.addObserver(observer);\n\t}\n\n\tpublic static synchronized Loggers create(String name) {\n\t\tSystem.out.println(\"create loggers \" + name);\n\t\treturn new Loggers(name);\n\t}\n\n\tprivate static LogLevel getLevel(final String fullName) {\n\t\tConcurrentMap<String, LogLevel> cache = _levelMap;\n\t\tif (cache.isEmpty()) {\n\t\t\treturn DEFAULT_LEVEL;\n\t\t}\n\t\tLogLevel level = cache.get(fullName);\n\t\tif (level != null) {\n\t\t\treturn level;\n\t\t}\n\t\tint index = 0;\n\t\tString logName = fullName;\n\t\tdo {\n\t\t\tindex = logName.lastIndexOf('.');\n\t\t\tif (index <= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlogName = logName.substring(0, index);\n\t\t\tlevel = cache.get(logName);\n\t\t\tif (level != null) {\n\t\t\t\tcache.put(fullName, level);\n\t\t\t\treturn level;\n\t\t\t}\n\t\t} while (logName.length() > 0);\n\t\tcache.put(fullName, DEFAULT_LEVEL);\n\t\treturn DEFAULT_LEVEL;\n\t}\n\n\tpublic static LogLevel getLevel(SumkLogger log) {\n\t\tString name = log.getName();\n\t\tif (name == null || name.isEmpty()) {\n\t\t\treturn DEFAULT_LEVEL;\n\t\t}\n\t\treturn getLevel(name);\n\t}\n\n\tpublic synchronized static void resetLevel(Map<String, LogLevel> newLevelMap) {\n\t\tConcurrentMap<String, LogLevel> newLevels = new ConcurrentHashMap<>(newLevelMap);\n\t\tLogLevel defaultLevel = newLevels.remove(ROOT);\n\t\tDEFAULT_LEVEL = defaultLevel == null ? LogLevel.INFO : defaultLevel;\n\t\t_levelMap = newLevels;\n\t}\n\n\tpublic static void setDefaultLevel(LogLevel level) {\n\t\tif (level != null) {\n\t\t\tDEFAULT_LEVEL = level;\n\t\t}\n\t}\n\n\tpublic static Map<String, LogLevel> currentLevels() {\n\t\treturn Collections.unmodifiableMap(_levelMap);\n\t}\n\n\tpublic SumkLogger get(String name) {\n\t\treturn map.get(name);\n\t}\n\n\tpublic SumkLogger putIfAbsent(String name, SumkLogger log) {\n\t\treturn map.putIfAbsent(name, log);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/Logs.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\nimport org.slf4j.Logger;\n\n/**\n * 这个类仅限于sumk框架内部使用\n */\npublic final class Logs {\n\n\tpublic static Logger http() {\n\t\treturn Log.get(\"sumk.http\");\n\t}\n\n\tpublic static Logger rpc() {\n\t\treturn Log.get(\"sumk.rpc\");\n\t}\n\n\tpublic static Logger system() {\n\t\treturn Log.get(\"sumk.system\");\n\t}\n\n\tpublic static Logger db() {\n\t\treturn Log.get(\"sumk.db\");\n\t}\n\n\tpublic static Logger redis() {\n\t\treturn Log.get(\"sumk.redis\");\n\t}\n\n\tpublic static Logger ioc() {\n\t\treturn Log.get(\"sumk.ioc\");\n\t}\n\n\tpublic static Logger asm() {\n\t\treturn Log.get(\"sumk.ioc.asm\");\n\t}\n\n\tpublic static Logger aop() {\n\t\treturn Log.get(\"sumk.ioc.aop\");\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/RawLog.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\nimport java.util.Objects;\n\npublic final class RawLog {\n\n\tpublic static final SimpleLogger CONSOLE_LOG = new SimpleLogger() {\n\t\t@Override\n\t\tpublic void debug(String module, String msg) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void info(String module, String msg) {\n\t\t\tSystem.out.println(msg);\n\t\t}\n\n\t\t@Override\n\t\tpublic void warn(String module, String msg) {\n\t\t\tSystem.out.println(msg);\n\t\t}\n\n\t\t@Override\n\t\tpublic void error(String module, String msg, Throwable e) {\n\t\t\tif (msg != null) {\n\t\t\t\tSystem.out.println(msg);\n\t\t\t}\n\t\t\tif (e != null) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void error(String module, String msg) {\n\t\t\tSystem.out.println(msg);\n\t\t}\n\t};\n\n\tpublic static final SimpleLogger SLF4J_LOG = new SimpleLogger() {\n\t\t@Override\n\t\tpublic void debug(String module, String msg) {\n\t\t\tLog.get(module).debug(msg);\n\t\t}\n\n\t\t@Override\n\t\tpublic void info(String module, String msg) {\n\t\t\tLog.get(module).info(msg);\n\t\t}\n\n\t\t@Override\n\t\tpublic void warn(String module, String msg) {\n\t\t\tLog.get(module).warn(msg);\n\t\t}\n\n\t\t@Override\n\t\tpublic void error(String module, String msg, Throwable e) {\n\t\t\tLog.get(module).error(msg, e);\n\t\t}\n\n\t\t@Override\n\t\tpublic void error(String module, String msg) {\n\t\t\tLog.get(module).error(msg);\n\t\t}\n\n\t};\n\n\tprivate static SimpleLogger inst = CONSOLE_LOG;\n\n\tpublic static void setLogger(SimpleLogger inst) {\n\t\tRawLog.inst = Objects.requireNonNull(inst);\n\t}\n\n\tpublic static void debug(String module, String msg) {\n\t\tinst.debug(module, msg);\n\t}\n\n\tpublic static void info(String module, String msg) {\n\t\tinst.info(module, msg);\n\t}\n\n\tpublic static void warn(String module, String msg) {\n\t\tinst.warn(module, msg);\n\t}\n\n\tpublic static void error(String module, String msg) {\n\t\tinst.error(module, msg);\n\t}\n\n\tpublic static void error(String module, Throwable e) {\n\t\tinst.error(module, e.getMessage(), e);\n\t}\n\n\tpublic static void error(String module, String msg, Throwable e) {\n\t\tif (e == null) {\n\t\t\tinst.error(module, msg);\n\t\t\treturn;\n\t\t}\n\t\tif (msg == null || msg.isEmpty()) {\n\t\t\tmsg = e.toString();\n\t\t}\n\t\tinst.error(module, msg, e);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/SimpleLogger.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\npublic interface SimpleLogger {\n\tvoid debug(String module, String msg);\n\n\tvoid info(String module, String msg);\n\n\tvoid warn(String module, String msg);\n\n\tvoid error(String module, String msg, Throwable e);\n\n\tvoid error(String module, String msg);\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/log/SumkLogger.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.log;\n\nimport static org.yx.log.LogLevel.DEBUG;\nimport static org.yx.log.LogLevel.ERROR;\nimport static org.yx.log.LogLevel.INFO;\nimport static org.yx.log.LogLevel.TRACE;\nimport static org.yx.log.LogLevel.WARN;\n\nimport org.slf4j.Logger;\nimport org.slf4j.Marker;\n\npublic abstract class SumkLogger implements Logger {\n\n\tprotected final String name;\n\n\t/**\n\t * 不为null，也不以.开头\n\t */\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tprotected SumkLogger(String module) {\n\t\tthis.name = parseName(module);\n\t}\n\n\tprivate String parseName(String module) {\n\t\tif (module == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\twhile (module.startsWith(\".\")) {\n\t\t\tmodule = module.substring(1);\n\t\t}\n\t\treturn module;\n\t}\n\n\tprotected boolean isLogable(LogLevel methodLevel) {\n\t\treturn methodLevel.ordinal() >= Loggers.getLevel(this).ordinal();\n\t}\n\n\tprotected abstract void output(Marker marker, LogLevel methodLevel, String format, Object... arguments);\n\n\tprotected abstract void output(Marker marker, LogLevel methodLevel, String msg, Throwable e);\n\n\tprivate void log(LogLevel methodLevel, String msg) {\n\t\tif (this.isLogable(methodLevel)) {\n\t\t\tthis.output(null, methodLevel, msg);\n\t\t}\n\t}\n\n\tprivate void log(LogLevel methodLevel, String format, Object... arguments) {\n\t\tif (this.isLogable(methodLevel)) {\n\t\t\tthis.output(null, methodLevel, format, arguments);\n\t\t}\n\t}\n\n\tprivate void log(LogLevel methodLevel, String msg, Throwable t) {\n\t\tif (this.isLogable(methodLevel)) {\n\t\t\tthis.output(null, methodLevel, msg, t);\n\t\t}\n\t}\n\n\tprivate void log(Marker marker, LogLevel methodLevel, String msg) {\n\t\tif (this.isLogable(methodLevel)) {\n\t\t\tthis.output(marker, methodLevel, msg);\n\t\t}\n\t}\n\n\tprivate void log(Marker marker, LogLevel methodLevel, String format, Object... arguments) {\n\t\tif (this.isLogable(methodLevel)) {\n\t\t\tthis.output(marker, methodLevel, format, arguments);\n\t\t}\n\t}\n\n\tprivate void log(Marker marker, LogLevel methodLevel, String msg, Throwable t) {\n\t\tif (this.isLogable(methodLevel)) {\n\t\t\tthis.output(marker, methodLevel, msg, t);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isTraceEnabled() {\n\t\treturn isLogable(TRACE);\n\t}\n\n\t@Override\n\tpublic void trace(String msg) {\n\t\tthis.log(TRACE, msg);\n\t}\n\n\t@Override\n\tpublic void trace(String format, Object arg) {\n\t\tthis.log(TRACE, format, arg);\n\t}\n\n\t@Override\n\tpublic void trace(String format, Object arg1, Object arg2) {\n\t\tthis.log(TRACE, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void trace(String format, Object... arguments) {\n\t\tthis.log(TRACE, format, arguments);\n\t}\n\n\t@Override\n\tpublic void trace(String msg, Throwable t) {\n\t\tthis.log(TRACE, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isDebugEnabled() {\n\t\treturn isLogable(DEBUG);\n\t}\n\n\t@Override\n\tpublic void debug(String msg) {\n\t\tthis.log(DEBUG, msg);\n\t}\n\n\t@Override\n\tpublic void debug(String format, Object arg) {\n\t\tthis.log(DEBUG, format, arg);\n\t}\n\n\t@Override\n\tpublic void debug(String format, Object arg1, Object arg2) {\n\t\tthis.log(DEBUG, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void debug(String format, Object... arguments) {\n\t\tthis.log(DEBUG, format, arguments);\n\t}\n\n\t@Override\n\tpublic void debug(String msg, Throwable t) {\n\t\tthis.log(DEBUG, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isInfoEnabled() {\n\t\treturn isLogable(INFO);\n\t}\n\n\t@Override\n\tpublic void info(String msg) {\n\t\tthis.log(INFO, msg);\n\t}\n\n\t@Override\n\tpublic void info(String format, Object arg) {\n\t\tthis.log(INFO, format, arg);\n\t}\n\n\t@Override\n\tpublic void info(String format, Object arg1, Object arg2) {\n\t\tthis.log(INFO, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void info(String format, Object... arguments) {\n\t\tthis.log(INFO, format, arguments);\n\t}\n\n\t@Override\n\tpublic void info(String msg, Throwable t) {\n\t\tthis.log(INFO, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isWarnEnabled() {\n\t\treturn isLogable(WARN);\n\t}\n\n\t@Override\n\tpublic void warn(String msg) {\n\t\tthis.log(WARN, msg);\n\t}\n\n\t@Override\n\tpublic void warn(String format, Object arg) {\n\t\tthis.log(WARN, format, arg);\n\t}\n\n\t@Override\n\tpublic void warn(String format, Object arg1, Object arg2) {\n\t\tthis.log(WARN, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void warn(String format, Object... arguments) {\n\t\tthis.log(WARN, format, arguments);\n\t}\n\n\t@Override\n\tpublic void warn(String msg, Throwable t) {\n\t\tthis.log(WARN, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isErrorEnabled() {\n\t\treturn isLogable(ERROR);\n\t}\n\n\t@Override\n\tpublic void error(String msg) {\n\t\tthis.log(ERROR, msg);\n\t}\n\n\t@Override\n\tpublic void error(String format, Object arg) {\n\t\tthis.log(ERROR, format, arg);\n\t}\n\n\t@Override\n\tpublic void error(String format, Object arg1, Object arg2) {\n\t\tthis.log(ERROR, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void error(String format, Object... arguments) {\n\t\tthis.log(ERROR, format, arguments);\n\t}\n\n\t@Override\n\tpublic void error(String msg, Throwable t) {\n\t\tthis.log(ERROR, msg, t);\n\t}\n\n\tpublic boolean isON() {\n\t\treturn Loggers.getLevel(this) == LogLevel.ON;\n\t}\n\n\t@Override\n\tpublic boolean isTraceEnabled(Marker marker) {\n\t\treturn this.isTraceEnabled();\n\t}\n\n\t@Override\n\tpublic void trace(Marker marker, String msg) {\n\t\tthis.log(marker, TRACE, msg);\n\t}\n\n\t@Override\n\tpublic void trace(Marker marker, String format, Object arg) {\n\t\tthis.log(marker, TRACE, format, arg);\n\t}\n\n\t@Override\n\tpublic void trace(Marker marker, String format, Object arg1, Object arg2) {\n\t\tthis.log(marker, TRACE, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void trace(Marker marker, String format, Object... argArray) {\n\t\tthis.log(marker, TRACE, format, argArray);\n\t}\n\n\t@Override\n\tpublic void trace(Marker marker, String msg, Throwable t) {\n\t\tthis.log(marker, TRACE, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isDebugEnabled(Marker marker) {\n\t\treturn this.isDebugEnabled();\n\t}\n\n\t@Override\n\tpublic void debug(Marker marker, String msg) {\n\t\tthis.log(marker, DEBUG, msg);\n\t}\n\n\t@Override\n\tpublic void debug(Marker marker, String format, Object arg) {\n\t\tthis.log(marker, DEBUG, format, arg);\n\t}\n\n\t@Override\n\tpublic void debug(Marker marker, String format, Object arg1, Object arg2) {\n\t\tthis.log(marker, DEBUG, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void debug(Marker marker, String format, Object... argArray) {\n\t\tthis.log(marker, DEBUG, format, argArray);\n\t}\n\n\t@Override\n\tpublic void debug(Marker marker, String msg, Throwable t) {\n\t\tthis.log(marker, DEBUG, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isInfoEnabled(Marker marker) {\n\t\treturn this.isInfoEnabled();\n\t}\n\n\t@Override\n\tpublic void info(Marker marker, String msg) {\n\t\tthis.log(marker, INFO, msg);\n\t}\n\n\t@Override\n\tpublic void info(Marker marker, String format, Object arg) {\n\t\tthis.log(marker, INFO, format, arg);\n\t}\n\n\t@Override\n\tpublic void info(Marker marker, String format, Object arg1, Object arg2) {\n\t\tthis.log(marker, INFO, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void info(Marker marker, String format, Object... argArray) {\n\t\tthis.log(marker, INFO, format, argArray);\n\t}\n\n\t@Override\n\tpublic void info(Marker marker, String msg, Throwable t) {\n\t\tthis.log(marker, INFO, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isWarnEnabled(Marker marker) {\n\t\treturn this.isWarnEnabled();\n\t}\n\n\t@Override\n\tpublic void warn(Marker marker, String msg) {\n\t\tthis.log(marker, WARN, msg);\n\t}\n\n\t@Override\n\tpublic void warn(Marker marker, String format, Object arg) {\n\t\tthis.log(marker, WARN, format, arg);\n\t}\n\n\t@Override\n\tpublic void warn(Marker marker, String format, Object arg1, Object arg2) {\n\t\tthis.log(marker, WARN, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void warn(Marker marker, String format, Object... argArray) {\n\t\tthis.log(marker, WARN, format, argArray);\n\t}\n\n\t@Override\n\tpublic void warn(Marker marker, String msg, Throwable t) {\n\t\tthis.log(marker, WARN, msg, t);\n\t}\n\n\t@Override\n\tpublic boolean isErrorEnabled(Marker marker) {\n\t\treturn this.isErrorEnabled();\n\t}\n\n\t@Override\n\tpublic void error(Marker marker, String msg) {\n\t\tthis.log(marker, ERROR, msg);\n\t}\n\n\t@Override\n\tpublic void error(Marker marker, String format, Object arg) {\n\t\tthis.log(marker, ERROR, format, arg);\n\t}\n\n\t@Override\n\tpublic void error(Marker marker, String format, Object arg1, Object arg2) {\n\t\tthis.log(marker, ERROR, format, arg1, arg2);\n\t}\n\n\t@Override\n\tpublic void error(Marker marker, String format, Object... argArray) {\n\t\tthis.log(marker, ERROR, format, argArray);\n\t}\n\n\t@Override\n\tpublic void error(Marker marker, String msg, Throwable t) {\n\t\tthis.log(marker, ERROR, msg, t);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.getClass().getSimpleName() + \"[\" + name + \"]\";\n\t}\n\n}"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/BitUtil.java",
    "content": "package org.yx.util;\n\nimport org.yx.exception.SumkException;\n\npublic class BitUtil {\n\n\tpublic static int setBit(int flag, int rightCount, boolean value) {\n\t\tif (rightCount < 1 || rightCount > 32) {\n\t\t\tthrow new SumkException(23436546, \"bit要在1-32之间，实际却是\" + rightCount);\n\t\t}\n\t\tint v = 1 << (rightCount - 1);\n\t\tif (value) {\n\t\t\treturn flag | v;\n\t\t}\n\t\treturn flag & (~v);\n\t}\n\n\tpublic static boolean getBit(int flag, int rightCount) {\n\t\tif (rightCount < 1 || rightCount > 32) {\n\t\t\tthrow new SumkException(23436546, \"bit要在1-32之间，实际却是\" + rightCount);\n\t\t}\n\t\tint v = 1 << (rightCount - 1);\n\t\treturn (flag & v) != 0;\n\t}\n\n\tpublic static int setBitsToTrue(int flag, int... rightCounts) {\n\t\tfor (int bit : rightCounts) {\n\t\t\tflag = setBit(flag, bit, true);\n\t\t}\n\t\treturn flag;\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/CollectionUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.util;\n\nimport static org.yx.conf.Const.LN;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.StringReader;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.base.sumk.UnmodifiableArrayList;\nimport org.yx.base.sumk.map.UnmodifiableListMap;\nimport org.yx.exception.SumkException;\nimport org.yx.log.RawLog;\n\n/**\n * 本类的许多方法都会对key、value做trim()处理\n */\npublic final class CollectionUtil {\n\n\tprivate static final String IGNORE_PREFIX = \"#\";\n\t/**\n\t * 与后面的有效行合并成一行。如果后面是空行或者#开头的行，就继续寻找下一行。 如果后面是文件结尾，就忽略这个结尾\n\t */\n\tprivate static final String LINE_CONCAT = \"\\\\\";\n\n\tpublic static Map<String, String> loadMapFromText(String text, String bigDelimiter, String smallDelimiter) {\n\t\treturn fillMapFromText(new HashMap<String, String>(), text, bigDelimiter, smallDelimiter);\n\t}\n\n\tpublic static Map<String, String> fillMapFromText(Map<String, String> map, String text, String bigDelimiter,\n\t\t\tString smallDelimiter) {\n\t\tfor (String entry : text.split(bigDelimiter)) {\n\t\t\tentry = entry.trim();\n\t\t\tif (StringUtil.isEmpty(entry)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString[] vs = entry.split(smallDelimiter, 2);\n\t\t\tswitch (vs.length) {\n\t\t\tcase 1:\n\t\t\t\tmap.put(vs[0].trim(), null);\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tmap.put(vs[0].trim(), vs[1].trim());\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n\tpublic static String saveMapToText(Map<String, ?> map, String bigDelimiter, String smallDelimiter) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (Entry<String, ?> entry : map.entrySet()) {\n\t\t\tString k = entry.getKey();\n\t\t\tObject v = entry.getValue();\n\t\t\tsb.append(k);\n\t\t\tif (v != null) {\n\t\t\t\tsb.append(smallDelimiter).append(v);\n\t\t\t}\n\t\t\tsb.append(bigDelimiter);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static Map<String, String> fillPropertiesFromText(final Map<String, String> map, String text) {\n\t\tif (text == null || text.isEmpty()) {\n\t\t\treturn map;\n\t\t}\n\t\ttry (BufferedReader reader = new BufferedReader(new StringReader(text))) {\n\t\t\tString tmp = null;\n\n\t\t\twhile ((tmp = readLine(reader)) != null) {\n\t\t\t\tString[] kv = tmp.split(\"=\", 2);\n\t\t\t\tif (kv.length != 2) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tString k = kv[0].trim();\n\t\t\t\tString v = kv[1].trim();\n\t\t\t\tif (k.isEmpty() || v.isEmpty()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmap.put(k, v);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tRawLog.error(\"文本转换成map失败：{}\", text);\n\t\t\tthrow new SumkException(-234352398, text, e);\n\t\t}\n\t\treturn map;\n\t}\n\n\t/**\n\t * \n\t * @param reader\n\t * @return 返回null表示读取结束\n\t * @throws IOException\n\t */\n\tprivate static String readLine(BufferedReader reader) throws IOException {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tString tmp = null;\n\t\tboolean readed = false;\n\n\t\twhile ((tmp = reader.readLine()) != null) {\n\t\t\treaded = true;\n\t\t\tif (tmp.startsWith(IGNORE_PREFIX) || tmp.trim().isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (tmp.endsWith(LINE_CONCAT)) {\n\t\t\t\tif (tmp.length() == 1) { // 1个字节如果做substring会出错\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tsb.append(tmp.substring(0, tmp.length() - 1));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsb.append(tmp);\n\t\t\tbreak;\n\t\t}\n\t\tif (!readed) {\n\t\t\treturn null;\n\t\t}\n\t\tString line = sb.toString();\n\t\treturn line.trim();\n\t}\n\n\tpublic static List<String> loadList(InputStream in) throws IOException {\n\t\tif (in == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tbyte[] bs = IOUtil.readAllBytes(in, true);\n\t\tif (bs == null || bs.length == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tString text = new String(bs, StandardCharsets.UTF_8);\n\t\ttext = StringUtil.formatNewLineFlag(text);\n\t\treturn StringUtil.splitAndTrim(text, LN);\n\t}\n\n\tpublic static boolean isEmpty(Map<?, ?> map) {\n\t\treturn map == null || map.isEmpty();\n\t}\n\n\tpublic static boolean isEmpty(Collection<?> colletion) {\n\t\treturn colletion == null || colletion.isEmpty();\n\t}\n\n\tpublic static boolean isNotEmpty(Collection<?> colletion) {\n\t\treturn colletion != null && colletion.size() > 0;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static Map<String, Object> flatMapToTree(@NotNull Map<String, String> map) {\n\t\tMap<String, Object> ret = new HashMap<>();\n\t\tfor (Entry<String, String> entry : map.entrySet()) {\n\t\t\tString k = entry.getKey();\n\t\t\tString v = entry.getValue();\n\t\t\tif (!k.contains(\".\")) {\n\t\t\t\tret.put(k, v);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString[] ks = k.split(\"\\\\.\");\n\t\t\tint lastIndex = ks.length - 1;\n\t\t\tMap<String, Object> temp = ret;\n\t\t\tfor (int i = 0; i < lastIndex; i++) {\n\t\t\t\tString k0 = ks[i];\n\t\t\t\tObject obj = temp.get(k0);\n\t\t\t\tif (obj == null) {\n\t\t\t\t\tMap<String, Object> temp2 = new HashMap<>();\n\t\t\t\t\ttemp.put(k0, temp2);\n\t\t\t\t\ttemp = temp2;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttemp = (Map<String, Object>) obj;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttemp.put(ks[lastIndex], v);\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\tpublic static <T> Map<String, T> subMap(@NotNull Map<String, T> source, @NotNull String prefix) {\n\t\tint len = prefix.length();\n\t\tMap<String, T> map = new HashMap<>();\n\t\tfor (Entry<String, T> entry : source.entrySet()) {\n\t\t\tString key = entry.getKey();\n\t\t\tT value = entry.getValue();\n\t\t\tif (key.startsWith(prefix)) {\n\t\t\t\tmap.put(key.substring(len), value);\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n\t/**\n\t * 返回一个不可变的list，这个list是原来的副本，它不会保存原来col的引用\n\t * \n\t * @param <T> 类型\n\t * @param col 原始集合，可以为null\n\t * @return 返回值不可修改，且不为null\n\t */\n\tpublic static <T> List<T> unmodifyList(Collection<T> col) {\n\t\tif (col == null || col.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (col instanceof UnmodifiableArrayList) {\n\t\t\treturn (UnmodifiableArrayList<T>) col;\n\t\t}\n\t\tif (col instanceof List) {\n\t\t\tString clzName = col.getClass().getName();\n\t\t\tif (\"java.util.Collections$SingletonList\".equals(clzName)\n\t\t\t\t\t|| \"java.util.Collections$UnmodifiableRandomAccessList\".equals(clzName)\n\t\t\t\t\t|| \"java.util.Collections$UnmodifiableList\".equals(clzName)) {\n\t\t\t\treturn (List<T>) col;\n\t\t\t}\n\t\t}\n\t\tif (col.size() == 1) {\n\t\t\treturn Collections.singletonList(col.iterator().next());\n\t\t}\n\t\treturn new UnmodifiableArrayList<>(col);\n\t}\n\n\t/**\n\t * 支持参数为null\n\t * \n\t * @param <T> 类型\n\t * @param arr 原始数组，对原始数组的修改有可能会修改本集合。它可以为null\n\t * @return 返回值不可修改，且不为null\n\t */\n\tpublic static <T> List<T> unmodifyList(T[] arr) {\n\t\tif (arr == null || arr.length == 0) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (arr.length == 1) {\n\t\t\treturn Collections.singletonList(arr[0]);\n\t\t}\n\t\treturn new UnmodifiableArrayList<>(arr);\n\t}\n\n\t/**\n\t * 生成不可变map。如果是特殊map，它的特性可能丢失，比如大小写不敏感特性\n\t * \n\t * @param <K> key的类型\n\t * @param <V> value的类型\n\t * @param m   原始map\n\t * @return 返回值不可修改，且不为null\n\t */\n\tpublic static <K, V> Map<K, V> unmodifyMap(Map<K, V> m) {\n\t\tif (m == null || m.isEmpty()) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tif (UnmodifiableListMap.class.equals(m.getClass())) {\n\t\t\treturn m;\n\t\t}\n\n\t\tString clzName = m.getClass().getName();\n\t\tif (\"java.util.Collections$SingletonMap\".equals(clzName)\n\t\t\t\t|| \"java.util.Collections$UnmodifiableMap\".equals(clzName)) {\n\t\t\treturn m;\n\t\t}\n\t\tfinal int size = m.size();\n\t\tif (size == 1) {\n\t\t\tEntry<K, V> kv = m.entrySet().iterator().next();\n\t\t\treturn Collections.singletonMap(kv.getKey(), kv.getValue());\n\t\t}\n\t\tif (size < 16) {\n\t\t\treturn new UnmodifiableListMap<>(m);\n\t\t}\n\t\treturn Collections.unmodifiableMap(m);\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/ExceptionUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.util;\n\nimport java.io.PrintWriter;\n\nimport org.yx.base.sumk.UnsafeStringWriter;\n\npublic final class ExceptionUtil {\n\n\tpublic static RuntimeException toRuntimeException(Throwable e) {\n\t\tif (e instanceof RuntimeException) {\n\t\t\treturn (RuntimeException) e;\n\t\t}\n\t\treturn new RuntimeException(e);\n\t}\n\n\tpublic static void printStackTrace(StringBuilder sb, Throwable e) {\n\t\tUnsafeStringWriter sw = new UnsafeStringWriter(sb);\n\t\tPrintWriter w = new PrintWriter(sw);\n\t\te.printStackTrace(w);\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/FileUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.util;\n\nimport java.io.File;\nimport java.net.URISyntaxException;\nimport java.util.Collection;\n\nimport org.yx.conf.AppInfo;\n\npublic final class FileUtil {\n\t/**\n\t * 列出该目录下的所有子文件（不包含目录）\n\t * \n\t * @param filelist 目标对象\n\t * @param parent   目录\n\t */\n\tpublic static void listAllSubFiles(Collection<File> filelist, File parent) {\n\t\tFile[] files = parent.listFiles();\n\t\tif (files != null) {\n\t\t\tfor (File f : files) {\n\t\t\t\tif (f.isDirectory()) {\n\t\t\t\t\tlistAllSubFiles(filelist, f);\n\t\t\t\t} else {\n\t\t\t\t\tfilelist.add(f);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n\n\tpublic static File file(String path) throws URISyntaxException {\n\t\tif (path.startsWith(AppInfo.CLASSPATH_URL_PREFIX)) {\n\t\t\treturn new File(Loader.loader().getResource(path.substring(AppInfo.CLASSPATH_URL_PREFIX.length())).toURI());\n\t\t}\n\t\treturn new File(path);\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/IOUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.Reader;\n\nimport org.yx.base.sumk.UnsafeByteArrayOutputStream;\nimport org.yx.log.RawLog;\n\npublic final class IOUtil {\n\n\tpublic static byte[] readAllBytes(InputStream in, boolean closeInput) throws IOException {\n\t\tUnsafeByteArrayOutputStream out = new UnsafeByteArrayOutputStream(1024);\n\t\ttransferTo(in, out, closeInput);\n\t\tout.close();\n\t\treturn out.toByteArray();\n\t}\n\n\tpublic static String readAll(Reader in, boolean closeInput) throws IOException {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tint len;\n\t\tchar[] buf = new char[1024];\n\t\ttry {\n\t\t\twhile ((len = in.read(buf)) > 0) {\n\t\t\t\tsb.append(buf, 0, len);\n\t\t\t}\n\t\t} finally {\n\t\t\tif (closeInput) {\n\t\t\t\ttry {\n\t\t\t\t\tin.close();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tRawLog.error(\"sumk.sys\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static int transferTo(InputStream in, OutputStream output, boolean closeInput) throws IOException {\n\t\tif (in == null) {\n\t\t\treturn 0;\n\t\t}\n\t\ttry {\n\t\t\tint n = 0;\n\t\t\tint count = 0;\n\t\t\tbyte[] temp = new byte[1024];\n\t\t\twhile (-1 != (n = in.read(temp))) {\n\t\t\t\toutput.write(temp, 0, n);\n\t\t\t\tcount += n;\n\t\t\t}\n\t\t\treturn count;\n\t\t} finally {\n\t\t\tif (closeInput) {\n\t\t\t\ttry {\n\t\t\t\t\tin.close();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tRawLog.error(\"sumk.sys\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/Loader.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Constructor;\nimport java.net.URL;\nimport java.util.Enumeration;\n\npublic final class Loader {\n\n\tpublic static final String JAVA_PRE = \"java.\";\n\tprivate static ClassLoader classLoader;\n\n\tpublic static void setClassLoader(ClassLoader classLoader) {\n\t\tLoader.classLoader = classLoader;\n\t}\n\n\tpublic static ClassLoader loader() {\n\t\tClassLoader l = classLoader;\n\t\treturn l != null ? l : Loader.class.getClassLoader();\n\t}\n\n\tpublic static <T> T newInstance(Class<T> clz) throws Exception {\n\t\tConstructor<T> c = clz.getDeclaredConstructor();\n\t\tif (!c.isAccessible()) {\n\t\t\tc.setAccessible(true);\n\t\t}\n\t\treturn c.newInstance();\n\t}\n\n\tpublic static Object newInstance(String clz) throws Exception {\n\t\tClass<?> clazz = loadClass(clz);\n\t\treturn newInstance(clazz);\n\t}\n\n\tpublic static Object newInstance(String clzName, Object[] params, Class<?>[] paramsType) throws Exception {\n\t\tClass<?> clz = Class.forName(clzName, true, loader());\n\t\tConstructor<?> c = paramsType == null ? clz.getDeclaredConstructor() : clz.getDeclaredConstructor(paramsType);\n\t\tif (!c.isAccessible()) {\n\t\t\tc.setAccessible(true);\n\t\t}\n\t\treturn c.newInstance(params);\n\t}\n\n\tpublic static Class<?> loadClass(String clz) throws ClassNotFoundException {\n\t\treturn loader().loadClass(clz);\n\t}\n\n\tpublic static InputStream getResourceAsStream(String name) {\n\t\treturn loader().getResourceAsStream(name);\n\t}\n\n\tpublic static Enumeration<URL> getResources(String name) throws IOException {\n\t\treturn loader().getResources(name.trim());\n\t}\n\n\tpublic static URL getResource(String name) {\n\t\treturn loader().getResource(name);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/StringUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.util;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.conf.Const;\nimport org.yx.exception.SumkException;\n\npublic final class StringUtil {\n\n\tpublic static String formatNewLineFlag(@NotNull String text) {\n\t\treturn text.replace(\"\\r\\n\", Const.LN).replace(\"\\r\", Const.LN);\n\t}\n\n\tpublic static String uncapitalize(String str) {\n\t\tint strLen;\n\t\tif (str == null || (strLen = str.length()) == 0) {\n\t\t\treturn str;\n\t\t}\n\t\treturn new StringBuilder(strLen).append(Character.toLowerCase(str.charAt(0))).append(str.substring(1))\n\t\t\t\t.toString();\n\t}\n\n\t/**\n\t * 这个会自动对每个子项做trim()操作，并且过滤掉空值\n\t * \n\t * @param source      原始字符串，不能为null\n\t * @param splitRegex  分隔符\n\t * @param otherSplits 其它的分隔符，这些分隔符不支持正则表达式。结果集会根据所有的分隔符进行分割\n\t * @return 结果不包含null和空字符串。返回值不为null\n\t */\n\tpublic static List<String> splitAndTrim(@NotNull String source, @NotNull String splitRegex, String... otherSplits) {\n\t\tif (otherSplits != null && otherSplits.length > 0) {\n\t\t\tfor (String r : otherSplits) {\n\t\t\t\tsource = source.replace(r, splitRegex);\n\t\t\t}\n\t\t}\n\t\tString[] vs = source.split(splitRegex);\n\t\tList<String> list = new ArrayList<>(vs.length);\n\t\tfor (String v : vs) {\n\t\t\tv = v.trim();\n\t\t\tif (v.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.add(v);\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic static boolean isEmpty(CharSequence str) {\n\t\treturn str == null || str.length() == 0;\n\t}\n\n\tpublic static String toLatin(String v) {\n\t\treturn v.replace('，', ',').replace('；', ';').replace('　', ' ').replace('：', ':').replace('。', '.').replace('？',\n\t\t\t\t'?');\n\t}\n\n\tpublic static String capitalize(String str) {\n\t\tint strLen;\n\t\tif (str == null || (strLen = str.length()) == 0) {\n\t\t\treturn str;\n\t\t}\n\t\treturn new StringBuilder(strLen).append(Character.toUpperCase(str.charAt(0))).append(str.substring(1))\n\t\t\t\t.toString();\n\t}\n\n\tpublic static boolean isNotEmpty(CharSequence str) {\n\t\treturn str != null && str.length() > 0;\n\t}\n\n\tpublic static String camelToUnderline(String param) {\n\t\tif (param == null) {\n\t\t\treturn param;\n\t\t}\n\t\tint len = param.length();\n\t\tStringBuilder sb = new StringBuilder(len + 10);\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tchar c = param.charAt(i);\n\t\t\tif (i == 0) {\n\t\t\t\tsb.append(Character.toLowerCase(c));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Character.isUpperCase(c)) {\n\t\t\t\tsb.append('_');\n\t\t\t\tsb.append(Character.toLowerCase(c));\n\t\t\t} else {\n\t\t\t\tsb.append(c);\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static boolean isNumber(char c) {\n\t\treturn c >= '0' && c <= '9';\n\t}\n\n\tpublic static String requireNotEmpty(String text) {\n\t\tif (text == null) {\n\t\t\tthrow new SumkException(652342134, \"字符串是null\");\n\t\t}\n\t\ttext = text.trim();\n\t\tif (text.isEmpty()) {\n\t\t\tthrow new SumkException(652342134, \"字符串是空的\");\n\t\t}\n\t\treturn text;\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/SumkDate.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.util;\n\nimport static java.time.temporal.ChronoField.DAY_OF_MONTH;\n\nimport java.io.Serializable;\nimport java.sql.Timestamp;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.time.DateTimeException;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.temporal.ChronoField;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Objects;\n\nimport org.yx.base.date.DateFormater;\nimport org.yx.base.date.DateTimeFormater;\nimport org.yx.base.date.FullDateTimeFormater;\nimport org.yx.base.date.SumkDateFormater;\nimport org.yx.base.date.SumkDateQuery;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\n\npublic final class SumkDate implements Comparable<SumkDate>, Serializable {\n\tprivate static final long serialVersionUID = 100L;\n\n\tpublic static final int SUMKDATE_ERROR_CODE = 912753954;\n\n\tprivate static final String LOG_NAME = \"sumk.date\";\n\n\tpublic static final String yyyy_MM_dd_HH_mm_ss = \"yyyy-MM-dd HH:mm:ss\";\n\n\tpublic static final String yyyy_MM_dd_HH_mm_ss_SSS = \"yyyy-MM-dd HH:mm:ss.SSS\";\n\n\tpublic static final String yyyy_MM_dd = \"yyyy-MM-dd\";\n\n\tpublic static final String HH_mm_ss = \"HH:mm:ss\";\n\n\tpublic static final String HH_mm_ss_SSS = \"HH:mm:ss.SSS\";\n\n\tprivate static final int MIL_TO_NANO = 1000_000;\n\tprivate static volatile CacheDate CACHED;\n\tprivate static int cacheChangeCount;\n\n\t/**\n\t * 只是个预估数字\n\t * \n\t * @return 缓存修改的次数\n\t */\n\tpublic static int cacheChangeCount() {\n\t\treturn cacheChangeCount;\n\t}\n\n\tprivate static final SumkDateFormater[] formaters = { FullDateTimeFormater.inst, DateTimeFormater.inst,\n\t\t\tDateFormater.inst };\n\n\t/**\n\t * @return 当前时间\n\t */\n\tpublic static SumkDate now() {\n\t\treturn create(System.currentTimeMillis(), true);\n\t}\n\n\tprivate static SumkDate create(final long currentMS, boolean toCache) {\n\t\tfinal long currentSeconds = currentMS / 1000;\n\t\tCacheDate cache = CACHED;\n\t\tif (cache != null) {\n\t\t\tif (currentSeconds == cache.seconds) {\n\t\t\t\treturn cache.date.withMilSecond((int) (currentMS % 1000));\n\t\t\t}\n\t\t\tlong newSec = currentSeconds - cache.seconds + cache.date.second;\n\t\t\tif (newSec >= 0 && newSec < 60) {\n\t\t\t\tSumkDate c = cache.date;\n\t\t\t\treturn new SumkDate(c.year, c.month, c.day, c.hour, c.minute, (byte) newSec,\n\t\t\t\t\t\t(short) (currentMS % 1000));\n\t\t\t}\n\t\t}\n\t\tCalendar.Builder builder = new Calendar.Builder();\n\t\tbuilder.setInstant(currentMS);\n\t\tSumkDate sd = of(builder.build());\n\t\tif (toCache) {\n\t\t\tCACHED = new CacheDate(currentSeconds, sd);\n\t\t\tcacheChangeCount++;\n\t\t}\n\t\treturn sd;\n\t}\n\n\tpublic static SumkDate of(Calendar cal) {\n\n\t\tint y = cal.get(Calendar.ERA) == GregorianCalendar.AD ? cal.get(Calendar.YEAR) : 1 - cal.get(Calendar.YEAR);\n\t\treturn of(y, cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DATE), cal.get(Calendar.HOUR_OF_DAY),\n\t\t\t\tcal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), cal.get(Calendar.MILLISECOND));\n\t}\n\n\tpublic static SumkDate of(Date d) {\n\t\treturn of(d.getTime());\n\t}\n\n\tpublic static SumkDate of(long timeInMillis) {\n\t\treturn create(timeInMillis, false);\n\t}\n\n\t/**\n\t * \n\t * @param year      年份，从公元元年开始计算\n\t * @param month     1-12\n\t * @param day       1-31\n\t * @param hour      0-23\n\t * @param minute    0-59\n\t * @param second    0-59\n\t * @param milSecond 0-999\n\t * @return SumkDate对象\n\t */\n\tpublic static SumkDate of(int year, int month, int day, int hour, int minute, int second, int milSecond) {\n\t\treturn new SumkDate(year, (byte) month, (byte) day, (byte) hour, (byte) minute, (byte) second,\n\t\t\t\t(short) milSecond);\n\t}\n\n\tSumkDate(int year, byte month, byte day, byte hour, byte minute, byte second, short milSecond) {\n\t\tif (month < 1 || month > 12) {\n\t\t\tthrow new SumkException(SUMKDATE_ERROR_CODE, month + \" is not valid month\");\n\t\t}\n\t\tif (day < 1 || day > 31) {\n\t\t\tthrow new SumkException(SUMKDATE_ERROR_CODE, day + \" is not valid day\");\n\t\t}\n\t\tif (hour < 0 || hour > 23) {\n\t\t\tthrow new SumkException(SUMKDATE_ERROR_CODE, hour + \" is not valid hour\");\n\t\t}\n\t\tif (minute < 0 || minute > 59) {\n\t\t\tthrow new SumkException(SUMKDATE_ERROR_CODE, minute + \" is not valid minute\");\n\t\t}\n\t\tif (second < 0 || second > 59) {\n\t\t\tthrow new SumkException(SUMKDATE_ERROR_CODE, second + \" is not valid second\");\n\t\t}\n\t\tif (milSecond < 0 || milSecond > 999) {\n\t\t\tthrow new SumkException(SUMKDATE_ERROR_CODE, milSecond + \" is not valid milSecond\");\n\t\t}\n\t\tthis.year = year;\n\t\tthis.month = month;\n\t\tthis.day = day;\n\t\tthis.hour = hour;\n\t\tthis.minute = minute;\n\t\tthis.second = second;\n\t\tthis.milSecond = milSecond;\n\t}\n\n\tpublic static SumkDate of(LocalDateTime d) {\n\t\treturn of(d.toLocalDate(), d.toLocalTime());\n\t}\n\n\t/**\n\t * @param date 可以为null，如果为null，年份就是1970-01-01\n\t * @param time 可以为null\n\t * @return SumkDate对象\n\t */\n\tpublic static SumkDate of(LocalDate date, LocalTime time) {\n\t\tint year, month, day;\n\t\tif (date != null) {\n\t\t\tyear = date.getYear();\n\t\t\tmonth = date.getMonthValue();\n\t\t\tday = date.getDayOfMonth();\n\t\t} else {\n\t\t\tyear = 1970;\n\t\t\tmonth = 1;\n\t\t\tday = 1;\n\t\t}\n\t\tif (time != null) {\n\t\t\treturn of(year, month, day, time.getHour(), time.getMinute(), time.getSecond(),\n\t\t\t\t\ttime.get(ChronoField.MILLI_OF_SECOND));\n\t\t}\n\t\treturn new SumkDate(year, (byte) month, (byte) day, (byte) 0, (byte) 0, (byte) 0, (byte) 0);\n\t}\n\n\tpublic static SumkDate of(final String dateString) {\n\t\tint lastDot = dateString.lastIndexOf('.');\n\n\t\tif (lastDot > 15 && dateString.length() - lastDot <= 4) {\n\t\t\treturn FullDateTimeFormater.inst.parse(dateString);\n\t\t}\n\t\treturn DateTimeFormater.inst.parse(dateString);\n\t}\n\n\tpublic static SumkDate of(final String dateString, final String format) {\n\t\tfor (SumkDateFormater f : formaters) {\n\t\t\tif (f.accept(format)) {\n\t\t\t\treturn f.parse(dateString);\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tDateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);\n\t\t\tSumkDate sd = formatter.parse(dateString, SumkDateQuery.inst);\n\t\t\tif (sd != null) {\n\t\t\t\treturn sd;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLog.get(LOG_NAME).warn(e.getMessage(), e);\n\t\t\tif (!AppInfo.getBoolean(\"sumk.date.retry_simple\", true)) {\n\t\t\t\tthrow new SumkException(SUMKDATE_ERROR_CODE, dateString + \"使用java time方式解析失败\", e);\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\treturn of(new SimpleDateFormat(format).parse(dateString));\n\t\t} catch (ParseException e1) {\n\t\t\tLog.get(LOG_NAME).error(e1.getMessage(), e1);\n\t\t\tthrow new SumkException(SUMKDATE_ERROR_CODE, dateString + \"使用SimpleDateFormat方式解析失败\", e1);\n\t\t}\n\n\t}\n\n\t/**\n\t * 将日期转化为字符串\n\t * \n\t * @param d      不能为null\n\t * @param format 格式\n\t * @return SumkDate对象，如果日期为null，就返回null\n\t */\n\tpublic static String format(Date d, String format) {\n\t\tif (d == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (yyyy_MM_dd_HH_mm_ss_SSS.equals(format)) {\n\t\t\treturn of(d).to_yyyy_MM_dd_HH_mm_ss_SSS();\n\t\t}\n\t\tif (yyyy_MM_dd_HH_mm_ss.equals(format)) {\n\t\t\treturn of(d).to_yyyy_MM_dd_HH_mm_ss();\n\t\t}\n\t\tif (yyyy_MM_dd.equals(format)) {\n\t\t\treturn of(d).to_yyyy_MM_dd();\n\t\t}\n\t\tif (HH_mm_ss.equals(format)) {\n\t\t\treturn of(d).to_HH_mm_ss();\n\t\t}\n\t\tif (HH_mm_ss_SSS.equals(format)) {\n\t\t\treturn of(d).to_HH_mm_ss_SSS();\n\t\t}\n\t\treturn new SimpleDateFormat(format).format(d);\n\t}\n\n\tpublic static String format(LocalDateTime d, String format) {\n\t\tif (d == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (yyyy_MM_dd_HH_mm_ss_SSS.equals(format)) {\n\t\t\treturn of(d).to_yyyy_MM_dd_HH_mm_ss_SSS();\n\t\t}\n\t\tif (yyyy_MM_dd_HH_mm_ss.equals(format)) {\n\t\t\treturn of(d).to_yyyy_MM_dd_HH_mm_ss();\n\t\t}\n\t\tif (yyyy_MM_dd.equals(format)) {\n\t\t\treturn of(d).to_yyyy_MM_dd();\n\t\t}\n\t\tif (HH_mm_ss.equals(format)) {\n\t\t\treturn of(d).to_HH_mm_ss();\n\t\t}\n\t\tif (HH_mm_ss_SSS.equals(format)) {\n\t\t\treturn of(d).to_HH_mm_ss_SSS();\n\t\t}\n\t\treturn d.format(DateTimeFormatter.ofPattern(format));\n\t}\n\n\tpublic static Date toDate(String dateString, String format) {\n\t\treturn of(dateString, format).toDate();\n\t}\n\n\tpublic static LocalDateTime toLocalDateTime(String dateString, String format) {\n\t\treturn of(dateString, format).toLocalDateTime();\n\t}\n\n\tpublic static Date toDate(LocalDateTime localDateTime) {\n\t\treturn of(localDateTime).toDate();\n\t}\n\n\tpublic static LocalDateTime toLocalDateTime(Date date) {\n\t\treturn of(date).toLocalDateTime();\n\t}\n\n\tpublic static long takeUpTimeInMils(SumkDate from, SumkDate to) {\n\t\treturn to.getTimeInMils() - from.getTimeInMils();\n\t}\n\n\tfinal int year;\n\n\tfinal byte month;\n\n\tfinal byte day;\n\n\tfinal byte hour;\n\n\tfinal byte minute;\n\n\tfinal byte second;\n\n\tfinal short milSecond;\n\n\t/**\n\t * @return 日期中的天，1-31\n\t * \n\t */\n\tpublic int getDay() {\n\t\treturn day;\n\t}\n\n\t/**\n\t * @return 24小时制，0-23\n\t */\n\tpublic int getHour() {\n\t\treturn hour;\n\t}\n\n\t/**\n\t * @return 毫秒，0-999\n\t */\n\tpublic int getMilSecond() {\n\t\treturn milSecond;\n\t}\n\n\t/**\n\t * @return 分钟，0-59\n\t */\n\tpublic int getMinute() {\n\t\treturn minute;\n\t}\n\n\t/**\n\t * @return 月份，1-12\n\t */\n\tpublic int getMonth() {\n\t\treturn month;\n\t}\n\n\t/**\n\t * @return 秒，0-59\n\t */\n\tpublic int getSecond() {\n\t\treturn second;\n\t}\n\n\t/**\n\t * @return 按公元纪年。公元前的年份可能会有问题，因为该年份不会小于0\n\t * \n\t */\n\tpublic int getYear() {\n\t\treturn year;\n\t}\n\n\t/**\n\t * 返回的格式如13:12:59\n\t * \n\t * @return HH:mm:ss 格式\n\t */\n\tpublic String to_HH_mm_ss() {\n\t\treturn new UnsafeFormater(this).to_HH_mm_ss().toString();\n\t}\n\n\t/**\n\t * 返回的格式如13:12:59.123\n\t * \n\t * @return HH:mm:ss.SSS 格式\n\t */\n\tpublic String to_HH_mm_ss_SSS() {\n\t\treturn new UnsafeFormater(this).to_HH_mm_ss_SSS().toString();\n\t}\n\n\t/**\n\t * 返回的格式如2018-10\n\t * \n\t * @return yyyy-MM格式 如果年份小于1000，会在年份前面补上0。与SimpleDateFormat兼容\n\t */\n\tpublic String to_yyyy_MM() {\n\t\treturn new UnsafeFormater(this).to_yyyy_MM().toString();\n\t}\n\n\t/**\n\t * 返回的格式如2018-10-20\n\t * \n\t * @return yyyy-MM-dd格式 如果年份小于1000，会在年份前面补上0。与SimpleDateFormat兼容\n\t */\n\tpublic String to_yyyy_MM_dd() {\n\t\treturn new UnsafeFormater(this).to_yyyy_MM_dd().toString();\n\t}\n\n\t/**\n\t * 返回的格式如2018-10-20 13:12:59\n\t * \n\t * @return yyyy-MM-dd HH:mm:ss 格式<BR>\n\t *         如果年份小于1000，会在年份前面补上0。与SimpleDateFormat兼容\n\t */\n\tpublic String to_yyyy_MM_dd_HH_mm_ss() {\n\t\treturn new UnsafeFormater(this).to_yyyy_MM_dd_HH_mm_ss().toString();\n\t}\n\n\t/**\n\t * 返回的格式如2018-10-20 13:12:59.123\n\t * \n\t * @return yyyy-MM-dd HH:mm:ss.SSS 格式 如果年份小于1000，会在年份前面补上0。与SimpleDateFormat兼容\n\t */\n\tpublic String to_yyyy_MM_dd_HH_mm_ss_SSS() {\n\t\treturn new UnsafeFormater(this).to_yyyy_MM_dd_HH_mm_ss_SSS().toString();\n\t}\n\n\tpublic Calendar toCalendar() {\n\t\tCalendar cal = Calendar.getInstance();\n\t\tcal.set(year, month - 1, day, hour, minute, second);\n\t\tcal.set(Calendar.MILLISECOND, milSecond);\n\t\treturn cal;\n\t}\n\n\tpublic Date toDate() {\n\t\treturn toCalendar().getTime();\n\t}\n\n\tpublic long getTimeInMils() {\n\t\treturn toCalendar().getTimeInMillis();\n\t}\n\n\tpublic LocalDate toLocalDate() {\n\t\treturn LocalDate.of(year, month, day);\n\t}\n\n\tpublic LocalDateTime toLocalDateTime() {\n\t\treturn LocalDateTime.of(toLocalDate(), toLocalTime());\n\t}\n\n\tpublic LocalTime toLocalTime() {\n\t\treturn LocalTime.of(hour, minute, second, milSecond * MIL_TO_NANO);\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tpublic Timestamp toTimestamp() {\n\t\treturn new Timestamp(getYear() - 1900, getMonth() - 1, getDay(), getHour(), getMinute(), getSecond(),\n\t\t\t\tthis.milSecond * 1_000_000);\n\t}\n\n\tpublic boolean isAfter(SumkDate d) {\n\t\treturn this.compareTo(d) > 0;\n\t}\n\n\tpublic boolean isBefore(SumkDate d) {\n\t\treturn this.compareTo(d) < 0;\n\t}\n\n\t/**\n\t * @return yyyy-MM-dd HH:mm:ss.SSS 格式 如果年份小于1000，会在年份前面补上0。与SimpleDateFormat兼容\n\t */\n\t@Override\n\tpublic String toString() {\n\t\treturn to_yyyy_MM_dd_HH_mm_ss_SSS();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + day;\n\t\tresult = prime * result + hour;\n\t\tresult = prime * result + milSecond;\n\t\tresult = prime * result + minute;\n\t\tresult = prime * result + month;\n\t\tresult = prime * result + second;\n\t\tresult = prime * result + year;\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tSumkDate other = (SumkDate) obj;\n\t\tif (day != other.day)\n\t\t\treturn false;\n\t\tif (hour != other.hour)\n\t\t\treturn false;\n\t\tif (milSecond != other.milSecond)\n\t\t\treturn false;\n\t\tif (minute != other.minute)\n\t\t\treturn false;\n\t\tif (month != other.month)\n\t\t\treturn false;\n\t\tif (second != other.second)\n\t\t\treturn false;\n\t\tif (year != other.year)\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int compareTo(SumkDate d) {\n\t\tif (d == null) {\n\t\t\treturn 1;\n\t\t}\n\n\t\tif (this.year != d.year) {\n\t\t\treturn year > d.year ? 1 : -1;\n\t\t}\n\n\t\tint v = this.month - d.month;\n\t\tif (v != 0) {\n\t\t\treturn v;\n\t\t}\n\n\t\tv = this.day - d.day;\n\t\tif (v != 0) {\n\t\t\treturn v;\n\t\t}\n\n\t\tv = this.hour - d.hour;\n\t\tif (v != 0) {\n\t\t\treturn v;\n\t\t}\n\n\t\tv = this.minute - d.minute;\n\t\tif (v != 0) {\n\t\t\treturn v;\n\t\t}\n\n\t\tv = this.second - d.second;\n\t\tif (v != 0) {\n\t\t\treturn v;\n\t\t}\n\n\t\treturn this.milSecond - d.milSecond;\n\t}\n\n\tpublic SumkDate withYear(int year) {\n\t\tif (year == this.year) {\n\t\t\treturn this;\n\t\t}\n\t\treturn new SumkDate(year, month, day, hour, minute, second, milSecond);\n\t}\n\n\t/**\n\t * 设置月份（不修改原来的对象）\n\t * \n\t * @param month 1-12\n\t * @return 新的SumkDate对象\n\t */\n\tpublic SumkDate withMonth(int month) {\n\t\tif (month == this.month) {\n\t\t\treturn this;\n\t\t}\n\t\tif (month < 1 || month > 12) {\n\t\t\tthrow new DateTimeException(\n\t\t\t\t\tnew StringBuilder().append(\"月份参数 \").append(month).append(\" 不在有效值范围内\").toString());\n\t\t}\n\t\treturn new SumkDate(year, (byte) month, day, hour, minute, second, milSecond);\n\t}\n\n\tpublic SumkDate withDay(int day) {\n\t\tif (day == this.day) {\n\t\t\treturn this;\n\t\t}\n\t\tDAY_OF_MONTH.checkValidValue(day);\n\t\treturn new SumkDate(year, month, (byte) day, hour, minute, second, milSecond);\n\t}\n\n\tpublic SumkDate withHour(int h) {\n\t\tif (h == this.hour) {\n\t\t\treturn this;\n\t\t}\n\t\tif (h < 0 || h > 23) {\n\t\t\tthrow new DateTimeException(new StringBuilder().append(\"小时参数 \").append(h).append(\" 不在有效值范围内\").toString());\n\t\t}\n\t\treturn new SumkDate(year, month, day, (byte) h, minute, second, milSecond);\n\t}\n\n\tpublic SumkDate withMinute(int m) {\n\t\tif (m == this.minute) {\n\t\t\treturn this;\n\t\t}\n\t\tif (m < 0 || m > 59) {\n\t\t\tthrow new DateTimeException(new StringBuilder().append(\"分钟参数 \").append(m).append(\" 不在有效值范围内\").toString());\n\t\t}\n\t\treturn new SumkDate(year, month, day, hour, (byte) m, second, milSecond);\n\t}\n\n\tpublic SumkDate withSecond(int sec) {\n\t\tif (sec == this.second) {\n\t\t\treturn this;\n\t\t}\n\t\tif (sec < 0 || sec > 59) {\n\t\t\tthrow new DateTimeException(new StringBuilder().append(\"秒参数 \").append(sec).append(\" 不在有效值范围内\").toString());\n\t\t}\n\t\treturn new SumkDate(year, month, day, hour, minute, (byte) sec, milSecond);\n\t}\n\n\tpublic SumkDate withMilSecond(int milSecond) {\n\t\tif (milSecond == this.milSecond) {\n\t\t\treturn this;\n\t\t}\n\t\tif (milSecond < 0 || milSecond > 999) {\n\t\t\tthrow new DateTimeException(\n\t\t\t\t\tnew StringBuilder().append(\"毫秒参数 \").append(milSecond).append(\" 不在有效值范围内\").toString());\n\t\t}\n\t\treturn new SumkDate(year, month, day, hour, minute, second, (short) milSecond);\n\t}\n\n\tpublic SumkDate plusYears(int years) {\n\t\tif (years == 0) {\n\t\t\treturn this;\n\t\t}\n\t\treturn new SumkDate(year + years, month, day, hour, minute, second, milSecond);\n\t}\n\n\t/**\n\t * months个月以后的日期\n\t * \n\t * @param months 任意数字，正数、负数都可以\n\t * @return months个月以后的日期\n\t */\n\tpublic SumkDate plusMonths(int months) {\n\t\tif (months == 0) {\n\t\t\treturn this;\n\t\t}\n\t\treturn of(this.toLocalDateTime().plusMonths(months));\n\t}\n\n\tpublic SumkDate plusDays(int days) {\n\t\tif (days == 0) {\n\t\t\treturn this;\n\t\t}\n\t\treturn of(this.toLocalDateTime().plusDays(days));\n\t}\n\n\tpublic SumkDate plusHours(int hours) {\n\t\tif (hours == 0) {\n\t\t\treturn this;\n\t\t}\n\t\treturn of(this.toLocalDateTime().plusHours(hours));\n\t}\n\n\tpublic SumkDate plusMinutes(int minutes) {\n\t\tif (minutes == 0) {\n\t\t\treturn this;\n\t\t}\n\t\treturn of(this.toLocalDateTime().plusMinutes(minutes));\n\t}\n\n\tpublic SumkDate plusSeconds(int seconds) {\n\t\tif (seconds == 0) {\n\t\t\treturn this;\n\t\t}\n\t\treturn of(this.toLocalDateTime().plusSeconds(seconds));\n\t}\n\n\tpublic SumkDate plusMilSeconds(int mils) {\n\t\tif (mils == 0) {\n\t\t\treturn this;\n\t\t}\n\t\treturn of(this.toLocalDateTime().plus(mils, ChronoUnit.MILLIS));\n\t}\n\n\tpublic static String stringOf(Date d) {\n\t\treturn d == null ? \"null\" : of(d).toString();\n\t}\n\n\tpublic static String stringOf(LocalDateTime d) {\n\t\treturn d == null ? \"null\" : of(d).toString();\n\t}\n\n\tprivate static final class CacheDate {\n\t\tfinal long seconds;\n\t\tfinal SumkDate date;\n\n\t\tCacheDate(long second, SumkDate date) {\n\t\t\tthis.seconds = second;\n\t\t\tthis.date = Objects.requireNonNull(date);\n\t\t}\n\t}\n\n\tprivate static final class UnsafeFormater {\n\t\tprivate static final char DATE_SPLIT = '-';\n\t\tprivate static final char TIME_SPLIT = ':';\n\n\t\tprivate final SumkDate sumkDate;\n\t\tprivate final StringBuilder sb = new StringBuilder(26);\n\n\t\tUnsafeFormater(SumkDate sumkDate) {\n\t\t\tthis.sumkDate = sumkDate;\n\t\t}\n\n\t\tvoid appendDoubleChar(byte v) {\n\t\t\tif (v < 10) {\n\t\t\t\tsb.append('0');\n\t\t\t}\n\t\t\tsb.append(v);\n\t\t}\n\n\t\t/**\n\t\t * 返回的格式如13:12:59\n\t\t */\n\t\tUnsafeFormater to_HH_mm_ss() {\n\t\t\tappendDoubleChar(sumkDate.hour);\n\t\t\tsb.append(TIME_SPLIT);\n\n\t\t\tappendDoubleChar(sumkDate.minute);\n\t\t\tsb.append(TIME_SPLIT);\n\n\t\t\tappendDoubleChar(sumkDate.second);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * 返回的格式如13:12:59.123\n\t\t */\n\t\tUnsafeFormater to_HH_mm_ss_SSS() {\n\t\t\tthis.to_HH_mm_ss();\n\t\t\tsb.append('.');\n\t\t\tint mil = sumkDate.milSecond;\n\t\t\tif (mil < 10) {\n\t\t\t\tsb.append(\"00\");\n\t\t\t} else if (mil < 100) {\n\t\t\t\tsb.append('0');\n\t\t\t}\n\t\t\tsb.append(mil);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * 返回的格式如2018-10<BR>\n\t\t * 如果年份小于1000，会在年份前面补上0。与SimpleDateFormat兼容\n\t\t */\n\t\tUnsafeFormater to_yyyy_MM() {\n\t\t\tint y = sumkDate.year;\n\t\t\tif (y == Integer.MIN_VALUE) {\n\t\t\t\tsb.append(y);\n\t\t\t} else {\n\t\t\t\tif (y < 0) {\n\t\t\t\t\tsb.append('-');\n\t\t\t\t\ty = -y;\n\t\t\t\t}\n\t\t\t\tif (y < 1000) {\n\t\t\t\t\tif (y >= 100) {\n\t\t\t\t\t\tsb.append('0');\n\t\t\t\t\t} else if (y >= 10) {\n\t\t\t\t\t\tsb.append(\"00\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsb.append(\"000\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsb.append(y);\n\t\t\t}\n\n\t\t\tsb.append(DATE_SPLIT);\n\t\t\tappendDoubleChar(sumkDate.month);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * 返回的格式如2018-10-20\n\t\t * \n\t\t * @return yyyy-MM-dd格式 如果年份小于1000，会在年份前面补上0。与SimpleDateFormat兼容\n\t\t */\n\t\tUnsafeFormater to_yyyy_MM_dd() {\n\t\t\tthis.to_yyyy_MM();\n\t\t\tsb.append(DATE_SPLIT);\n\t\t\tappendDoubleChar(sumkDate.day);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * 返回的格式如2018-10-20 13:12:59\n\t\t * \n\t\t * @return yyyy-MM-dd HH:mm:ss 格式<BR>\n\t\t *         如果年份小于1000，会在年份前面补上0。与SimpleDateFormat兼容\n\t\t */\n\t\tUnsafeFormater to_yyyy_MM_dd_HH_mm_ss() {\n\t\t\tthis.to_yyyy_MM_dd();\n\t\t\tsb.append(' ');\n\t\t\tthis.to_HH_mm_ss();\n\t\t\treturn this;\n\t\t}\n\n\t\tUnsafeFormater to_yyyy_MM_dd_HH_mm_ss_SSS() {\n\t\t\tthis.to_yyyy_MM_dd();\n\t\t\tsb.append(' ');\n\t\t\tthis.to_HH_mm_ss_SSS();\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n}"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/SumkThreadPool.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.util;\n\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger;\nimport org.yx.base.context.AppContext;\nimport org.yx.base.thread.SumkExecutorService;\nimport org.yx.base.thread.ThreadPools;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.log.RawLog;\n\npublic final class SumkThreadPool {\n\n\tprivate static boolean daemon;\n\n\tpublic static void setDaemon(boolean daemon) {\n\t\tSumkThreadPool.daemon = daemon;\n\t}\n\n\tpublic static ThreadFactory createThreadFactory(String pre) {\n\t\treturn new ThreadFactory() {\n\t\t\tprivate final AtomicInteger threadNumber = new AtomicInteger(1);\n\n\t\t\t@Override\n\t\t\tpublic Thread newThread(Runnable r) {\n\t\t\t\tThread t = new Thread(r, pre + threadNumber.getAndIncrement());\n\t\t\t\tt.setDaemon(daemon);\n\t\t\t\tif (t.getPriority() != Thread.NORM_PRIORITY) {\n\t\t\t\t\tt.setPriority(Thread.NORM_PRIORITY);\n\t\t\t\t}\n\t\t\t\treturn t;\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate static final ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor(\n\t\t\tInteger.getInteger(\"sumk.thread.schedule.size\", 1), r -> {\n\t\t\t\tThread t = new Thread(r, \"sumk-task\");\n\t\t\t\tt.setDaemon(true);\n\t\t\t\tif (t.getPriority() != Thread.NORM_PRIORITY) {\n\t\t\t\t\tt.setPriority(Thread.NORM_PRIORITY);\n\t\t\t\t}\n\t\t\t\treturn t;\n\t\t\t});\n\n\tstatic ScheduledExecutorService scheduledExecutor() {\n\t\treturn scheduledExecutor;\n\t}\n\n\tpublic static SumkExecutorService executor() {\n\t\treturn ThreadPools.DEFAULT_EXECUTOR;\n\t}\n\n\tpublic static ScheduledFuture<?> scheduleAtFixedRate(Runnable job, long delayMS, long periodMS) {\n\t\tif (AppContext.inst().isDestoryed()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (periodMS <= 0) {\n\t\t\tthrow new SumkException(5674354, \"delayMils要大于0 才行\");\n\t\t} else if (periodMS < 100) {\n\t\t\tRawLog.warn(\"sumk.thread\", job + \"加入到作业中(短作业)，定时间隔为\" + periodMS + \"ms\");\n\t\t} else {\n\t\t\tRawLog.info(\"sumk.thread\", job + \"加入到作业中，定时间隔为\" + periodMS + \"ms\");\n\t\t}\n\t\tSynchronizedRunner single = new SynchronizedRunner(job);\n\t\tRunnable task = () -> {\n\n\t\t\tif (single.isBusy()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\texecutor().execute(single);\n\t\t\t\tsingle.working = 1;\n\t\t\t} catch (Exception e) {\n\t\t\t\tRawLog.error(\"sumk.thread\", \"添加定时任务失败\", e);\n\t\t\t}\n\t\t};\n\t\treturn scheduledExecutor.scheduleAtFixedRate(task, delayMS, periodMS, TimeUnit.MILLISECONDS);\n\t}\n\n\tpublic static ScheduledFuture<?> schedule(Runnable job, long delayMS) {\n\t\treturn scheduledExecutor.schedule(() -> executor().execute(job), delayMS, TimeUnit.MILLISECONDS);\n\t}\n\n\tpublic static void shutdown() {\n\t\tscheduledExecutor.shutdown();\n\t\texecutor().shutdown();\n\t}\n\n\tpublic static Runnable synchronize(Runnable target) {\n\t\treturn new SynchronizedRunner(target);\n\t}\n\n\tprivate static final class SynchronizedRunner implements Runnable {\n\n\t\tprivate final ReentrantLock lock;\n\t\tprivate final Runnable target;\n\n\t\tint working;\n\n\t\tpublic SynchronizedRunner(Runnable target) {\n\t\t\tthis.target = target;\n\t\t\tthis.lock = new ReentrantLock();\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif (!lock.tryLock()) {\n\t\t\t\tthis.working = 0;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\ttarget.run();\n\t\t\t} catch (Throwable e) {\n\t\t\t\tRawLog.error(\"sumk.thread\", target + \"执行失败，\" + e.getLocalizedMessage(), e);\n\t\t\t} finally {\n\t\t\t\tthis.working = 0;\n\t\t\t\tlock.unlock();\n\t\t\t}\n\t\t}\n\n\t\tpublic boolean isBusy() {\n\t\t\tif (lock.isLocked()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (this.working != 0) {\n\t\t\t\tthis.working++;\n\t\t\t\tif (this.working > 50) {\n\t\t\t\t\tthis.working = 0;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic static void scheduleThreadPoolMonitor() {\n\t\tlong period = AppInfo.getLong(\"sumk.threadpool.task.period\", 10_000);\n\n\t\tscheduledExecutor.scheduleAtFixedRate(new ThreadPoolReSeter(), period, period, TimeUnit.MILLISECONDS);\n\t}\n\n\tprivate static class ThreadPoolReSeter implements Runnable {\n\n\t\tprivate Logger logger = Log.get(\"sumk.thread\");\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\ttry {\n\t\t\t\tresetCurrentThreshold();\n\t\t\t\tresetThreadPoolSize();\n\t\t\t} catch (Exception e) {\n\t\t\t\tlogger.error(e.getLocalizedMessage(), e);\n\t\t\t}\n\t\t}\n\n\t\tprivate void resetCurrentThreshold() {\n\n\t\t\tint threshold = AppInfo.getInt(\"sumk.core.threadpool.threshold\", 0);\n\t\t\tSumkExecutorService executor = SumkThreadPool.executor();\n\t\t\tif (threshold > 0) {\n\t\t\t\texecutor.threshold(threshold);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthreshold = executor.getPoolSize() + executor.getQueued();\n\t\t\texecutor.threshold(threshold);\n\t\t\tlogger.trace(\"set pool threshold to {}\", threshold);\n\t\t}\n\n\t\tprivate void resetThreadPoolSize() {\n\t\t\tSumkExecutorService pool = SumkThreadPool.executor();\n\t\t\tint size = AppInfo.getInt(\"sumk.core.threadpool.core\", 0);\n\t\t\tif (size > 0 && pool.getCorePoolSize() != size) {\n\t\t\t\tlogger.info(\"change ThreadPool size from {} to {}\", pool.getCorePoolSize(), size);\n\t\t\t\tpool.setCorePoolSize(size);\n\t\t\t}\n\n\t\t\tsize = AppInfo.getInt(\"sumk.core.threadpool.max\", 0);\n\t\t\tif (size > 0 && pool.getMaximumPoolSize() != size) {\n\t\t\t\tlogger.info(\"change ThreadPool max size from {} to {}\", pool.getMaximumPoolSize(), size);\n\t\t\t\tpool.setMaximumPoolSize(size);\n\t\t\t}\n\n\t\t\tString v = AppInfo.get(\"sumk.core.threadpool.allowCoreThreadTimeOut\", null);\n\t\t\tif (v != null) {\n\t\t\t\tboolean allowCoreTimeout = \"1\".equals(v) || \"true\".equalsIgnoreCase(v);\n\t\t\t\tif (allowCoreTimeout != pool.allowsCoreThreadTimeOut()) {\n\t\t\t\t\tlogger.info(\"change ThreadPool allowsCoreThreadTimeOut from {} to {}\",\n\t\t\t\t\t\t\tpool.allowsCoreThreadTimeOut(), allowCoreTimeout);\n\t\t\t\t\tpool.allowCoreThreadTimeOut(allowCoreTimeout);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/Task.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.util;\n\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 这个定时任务与jvm自带的不同点在于：<BR>\n * 1、它不会因为异常而停止。<BR>\n * 2、它使用一个线程负责定时器，触发后由主线程池运行。但它也保证任务不会被并发<BR>\n */\n\npublic final class Task {\n\n\tpublic static ScheduledFuture<?> scheduleAtFixedRate(Runnable job, long delayMS, long periodMS) {\n\t\treturn SumkThreadPool.scheduleAtFixedRate(job, delayMS, periodMS);\n\t}\n\n\tpublic static ScheduledFuture<?> schedule(Runnable job, long delayMS) {\n\t\treturn SumkThreadPool.schedule(job, delayMS);\n\t}\n\n\tpublic static ScheduledFuture<?> scheduleAtFixedRate(Runnable job, long delay, long period, TimeUnit unit) {\n\t\treturn scheduleAtFixedRate(job, unit.toMillis(delay), unit.toMillis(period));\n\t}\n}\n"
  },
  {
    "path": "sumk-base/src/main/java/org/yx/util/UUIDSeed.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.util;\n\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic final class UUIDSeed {\n\tprivate static final char[] LETTERS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',\n\t\t\t'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };\n\tprivate static final int LEN = LETTERS.length;\n\tprivate static final int DOUBLE_LEN = LEN * LEN;\n\tprivate static AtomicInteger current = new AtomicInteger(6538);\n\n\tstatic void fill(char[] source, final int from, int bytes, long number) {\n\t\tif (number < 0) {\n\t\t\tnumber = Math.max(0 - number, 0);\n\t\t}\n\t\tint index = from + bytes - 1;\n\t\twhile (number > 0) {\n\t\t\tint k = (int) (number % LEN);\n\t\t\tsource[index] = LETTERS[k];\n\t\t\tif (index == from) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tnumber = number / LEN;\n\t\t\tindex--;\n\t\t}\n\t\twhile (index >= from) {\n\t\t\tsource[index--] = '0';\n\t\t}\n\t}\n\n\tpublic static long toLong(String s) {\n\t\ts = s.toLowerCase();\n\t\tString all = new String(LETTERS);\n\t\tlong ret = 0;\n\t\tfor (int i = s.length() - 1, k = 0; i >= 0; i--, k++) {\n\t\t\tchar c = s.charAt(i);\n\t\t\tint v = all.indexOf(String.valueOf(c));\n\t\t\tret += (v * Math.pow(LEN, k));\n\t\t}\n\t\treturn ret;\n\t}\n\n\tpublic static long getSeqTime(String sn) {\n\t\treturn toLong(sn.substring(0, 8));\n\t}\n\n\tpublic static long getRandomSNTime(String sn) {\n\t\tchar[] k = sn.toCharArray();\n\t\tString d = new String(new char[] { k[8], k[12], k[16], k[1], k[5], k[9], k[13], k[17] });\n\t\treturn toLong(d);\n\t}\n\n\tpublic static String seq() {\n\t\treturn new String(seqChars());\n\t}\n\n\tpublic static String seq18() {\n\t\treturn new String(seqChars(), 1, 18);\n\t}\n\n\tpublic static String random() {\n\t\treturn reOrder(seqChars());\n\t}\n\n\tstatic char[] seqChars() {\n\t\tThreadLocalRandom r = ThreadLocalRandom.current();\n\t\tchar[] ret = new char[20];\n\t\tfill(ret, 0, 8, System.currentTimeMillis());\n\t\tfill(ret, 8, 4, System.nanoTime());\n\t\tfill(ret, 12, 2, r.nextInt(DOUBLE_LEN));\n\n\t\tint addNum = r.nextInt(LEN) + 1;\n\t\tint next = current.addAndGet(addNum);\n\t\tif (next > 1000000000) {\n\t\t\tcurrent = new AtomicInteger(next % (DOUBLE_LEN * DOUBLE_LEN) + DOUBLE_LEN);\n\t\t}\n\n\t\tfill(ret, 14, 4, next);\n\t\tfill(ret, 18, 1, addNum);\n\t\tfill(ret, 19, 1, r.nextInt(LEN));\n\t\treturn ret;\n\t}\n\n\tstatic String reOrder(char[] cs) {\n\t\tchar g1 = cs[12];\n\t\tchar g2 = cs[13];\n\t\tSystem.arraycopy(cs, 0, cs, 2, 8);\n\t\tcs[0] = g1;\n\t\tcs[1] = g2;\n\t\tchar[] temp = new char[cs.length];\n\t\tint len = cs.length / 2;\n\t\tfor (int k = 0; k < 2; k++) {\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\ttemp[i * 2] = cs[i];\n\t\t\t\ttemp[2 * i + 1] = cs[i + len];\n\t\t\t}\n\t\t\tcs = temp;\n\t\t\ttemp = new char[cs.length];\n\t\t}\n\t\treturn new String(cs);\n\t}\n\n\tpublic static String parse(long number, int bytes) {\n\t\tchar[] cs = new char[bytes];\n\t\tfill(cs, 0, bytes, number);\n\t\treturn new String(cs);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright youtongluan (\\u6e38\\u901a\\u92ae\\uff0c\\u522b\\u540d\\uff1a\\u6e38\\u590f)\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "sumk-db/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.github.youtongluan</groupId>\n\t\t<artifactId>sumk</artifactId>\n\t\t<version>4.2.1</version>\n\t</parent>\n\t<artifactId>sumk-db</artifactId>\n\t<name>com.github.youtongluan:sumk</name>\n\t<description>A quick developing framewort for internet company</description>\n\t<url>https://github.com/youtongluan/sumk</url>\n\t<licenses>\n\t\t<license>\n\t\t\t<name>The Apache License, Version 2.0</name>\n\t\t\t<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n\t\t</license>\n\t</licenses>\n\t<scm>\n\t\t<url>https://github.com/youtongluan/sumk</url>\n\t\t<connection>https://github.com/youtongluan/sumk.git</connection>\n\t\t<developerConnection>https://github.com/youtongluan/sumk</developerConnection>\n\t</scm>\n\t<distributionManagement>\n\t    <repository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n\t    </repository>\n\t    <snapshotRepository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n\t    </snapshotRepository>\n\t</distributionManagement>\n\t<developers>\n\t\t<developer>\n\t\t\t<name>youtongluan</name>\n\t\t\t<email>3205207767@qq.com</email>\n\t\t\t<url>https://www.oschina.net/p/sumk</url>\n\t\t</developer>\n\t</developers>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>sumk-framework</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>async-logger</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.mybatis</groupId>\n\t\t\t<artifactId>mybatis</artifactId>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t<artifactId>commons-dbcp2</artifactId>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<artifactId>commons-logging</artifactId>\n\t\t\t\t\t<groupId>commons-logging</groupId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>mysql</groupId>\n\t\t\t<artifactId>mysql-connector-java</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>redis.clients</groupId>\n\t\t\t<artifactId>jedis</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/annotation/db/AutoCreateTime.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.db;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 在@Table注解的表中，让插入记录的时候，一并写入创建时间。它有以下要点:\n * <OL>\n * <LI>它注解的字段要是时间类型，比如Timestamp、Date、LocalDatetime</LI>\n * <LI>只有在单主键，并且该主键是Long类型时才有用</LI>\n * <LI>不限制一张表有几个CreateTime字段，0个或多个都是可以的</LI>\n * <LI>它只作用在insert的时候，之后它就跟其它字段没啥差异</LI>\n * </OL>\n */\n@Target({ ElementType.FIELD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface AutoCreateTime {\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/annotation/db/Box.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.db;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.yx.conf.Const;\nimport org.yx.db.enums.DBType;\nimport org.yx.db.enums.TransactionType;\n\n/**\n * 注解在当前类的public或protected方法上。不支持在超类或接口上进行注解\n */\n@Target({ ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Box {\n\n\tString value() default Const.DEFAULT_DB_NAME;\n\n\tDBType dbType() default DBType.ANY;\n\n\tTransactionType transaction() default TransactionType.REQUIRED;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/annotation/db/Column.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.db;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.yx.base.Ordered;\nimport org.yx.db.enums.ColumnType;\n\n@Target({ ElementType.FIELD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Column {\n\t/**\n\t * @return 数据库字段的名字，不填的话，就是属性名(小写)\n\t */\n\tString value() default \"\";\n\n\tColumnType type() default ColumnType.NORMAL;\n\n\tbyte order() default Ordered.DEFAULT_ORDER;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/annotation/db/SoftDelete.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.db;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.yx.db.enums.ValidRecord;\n\n/**\n * 有这个标志，就说明是软删除<BR>\n * 支持pojo中存在与软删除同名的字段（大小写不敏感），通常只在whatIsValid 这时insert和udpate的时候，要注意该字段的状态位。\n * \n * @author 游夏\n *\n */\n@Target({ ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface SoftDelete {\n\n\t/**\n\t * @return 数据库中字段的名字\n\t */\n\tString value();\n\n\t/**\n\t * @return 只能是String、Integer、Byte、Short、Long、Boolean或者它们的原始类型\n\t */\n\tClass<?> type() default String.class;\n\n\t/**\n\t * @return 如果是数字类型，会被转化成数字类型<BR>\n\t *         如果是Boolean类型，这个属性没有意义\n\t * \n\t */\n\tString validValue() default \"1\";\n\n\t/**\n\t * @return 如果是数字类型，会被转化成数字类型<BR>\n\t *         如果是Boolean类型，这个属性没有意义\n\t * \n\t */\n\tString inValidValue() default \"0\";\n\n\tValidRecord whatIsValid() default ValidRecord.EQUAL_VALID;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/annotation/db/Table.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.db;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.yx.db.enums.CacheType;\n\n@Target({ ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Table {\n\t/**\n\t * @return 表名。为空时，就是表名，支持#或者?作为通配符\n\t */\n\tString value() default \"\";\n\n\t/**\n\t * @return 在缓存中保留的时间,单位秒。0表示使用全局设置，小于0表示不过期\n\t */\n\tint duration() default 0;\n\n\t/**\n\t * 如果使用cluster，同一张表的DB缓存会在一个slot上，这是为了防止mget、mset出问题\n\t * \n\t * @return 为空使用表名，一般使用默认就好。支持#或者?作为通配符\n\t */\n\tString preInCache() default \"\";\n\n\t/**\n\t * @return 访问多少次之后刷新缓存，0表示使用全局默认，小于0表示不刷新\n\t */\n\tint maxHit() default 0;\n\n\t/**\n\t * @return 主键缓存都是SINGLE，外键缓存一般用LIST\n\t */\n\tCacheType cacheType() default CacheType.SINGLE;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/DB.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db;\n\nimport java.sql.SQLException;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport org.yx.db.conn.ConnectionPool;\nimport org.yx.db.conn.HookContext;\nimport org.yx.db.enums.TxHook;\nimport org.yx.db.exec.DBExecutor;\nimport org.yx.db.exec.DBSource;\nimport org.yx.db.exec.DBTransaction;\nimport org.yx.db.sql.DBFactory;\nimport org.yx.db.sql.Delete;\nimport org.yx.db.sql.Insert;\nimport org.yx.db.sql.Select;\nimport org.yx.db.sql.Update;\nimport org.yx.util.ExceptionUtil;\n\n/**\n * ORM的入口。 本类如果使用Map做参数，map中的key一律是java字段名。<BR>\n * 大小写敏感性的原则是：数据库字段大小写不敏感，java字段大小写敏感。所以本类中的参数大小写敏感\n * \n * @author 游夏\n *\n */\npublic final class DB {\n\t/**\n\t * 进行插入，如果主键是单主键，并且主键是Long类型。 可以不用显示设置主键，系统会自动生成主键<BR>\n\t * 要执行execute方法才能生效\n\t * \n\t * @return Insert对象\n\t */\n\tpublic static Insert insert() {\n\t\treturn DBFactory.insert();\n\t}\n\n\t/**\n\t * 将pojo插入到数据库中<BR>\n\t * 要执行execute方法才能生效\n\t * \n\t * @param pojo pojo、map或pojo所对应的class对象\n\t * @return Insert对象\n\t */\n\tpublic static Insert insert(Object pojo) {\n\t\tif (pojo instanceof Class) {\n\t\t\treturn insert().tableClass((Class<?>) pojo);\n\t\t}\n\t\treturn insert().insert(pojo);\n\t}\n\n\t/**\n\t * 默认是局部更新，要调用fullUpdate()，才能进行全部更新。所有的更新都只能根据主键或者redis主键<BR>\n\t * 要执行execute方法才能生效<BR>\n\t * 如果显式指定where条件，每个条件里都必须包含所有的redis主键字段\n\t * \n\t * @return Update对象\n\t */\n\tpublic static Update update() {\n\t\treturn DBFactory.update();\n\t}\n\n\t/**\n\t * 默认是局部更新，要调用fullUpdate()，才能进行全部更新。<BR>\n\t * 要执行execute方法才能生效<BR>\n\t * 如果显式指定where条件，每个条件里都必须包含所有的redis主键字段\n\t * \n\t * @param pojo 修改后的pojo值，如果没有显式设置where条件，那么它的条件就是数据库主键或者redis主键\n\t * @return Update对象\n\t */\n\tpublic static Update update(Object pojo) {\n\t\tif (pojo instanceof Class) {\n\t\t\treturn update().tableClass((Class<?>) pojo);\n\t\t}\n\t\treturn update().updateTo(pojo);\n\t}\n\n\tpublic static Delete delete() {\n\t\treturn DBFactory.delete();\n\t}\n\n\t/**\n\t * 删除（包括软删除）pojo对象定义<BR>\n\t * 要执行execute方法才能生效\n\t * \n\t * @param pojo pojo、map或pojo所对应的class对象\n\t * @return Delete对象\n\t */\n\tpublic static Delete delete(Object pojo) {\n\t\tif (pojo instanceof Class) {\n\t\t\treturn delete().tableClass((Class<?>) pojo);\n\t\t}\n\t\treturn delete().delete(pojo);\n\t}\n\n\tpublic static Select select() {\n\t\treturn DBFactory.select();\n\t}\n\n\tpublic static Select select(Object pojo) {\n\t\tif (pojo instanceof Class) {\n\t\t\treturn select().tableClass((Class<?>) pojo);\n\t\t}\n\t\treturn select().addEqual(pojo);\n\t}\n\n\tpublic static void commit() throws SQLException {\n\t\tConnectionPool.get().commit();\n\t}\n\n\tpublic static void rollback(Exception e) throws SQLException {\n\t\tConnectionPool.get().rollback(e);\n\t}\n\n\tpublic static void addHook(TxHook type, Consumer<HookContext> consumer) {\n\t\tConnectionPool.get().addHook(type, consumer);\n\t}\n\n\tpublic static <T> T execute(DBSource ds, DBExecutor<T> executor) throws RuntimeException {\n\t\tDBTransaction tran = new DBTransaction(Objects.requireNonNull(ds));\n\t\tT ret = null;\n\t\ttry {\n\t\t\ttran.begin();\n\t\t\tret = executor.execute(ds);\n\t\t\ttran.commit();\n\t\t} catch (Exception e) {\n\t\t\ttran.rollback(e);\n\t\t\tthrow ExceptionUtil.toRuntimeException(e);\n\t\t} finally {\n\t\t\ttran.close();\n\t\t}\n\t\treturn ret;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/DBJson.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db;\n\nimport java.util.Objects;\n\nimport org.yx.common.json.ByteArrayTypeAdapter;\nimport org.yx.common.json.GsonHelper;\nimport org.yx.common.json.GsonOperator;\nimport org.yx.common.json.JsonOperator;\n\npublic final class DBJson {\n\n\tprivate static JsonOperator operator = new GsonOperator(\n\t\t\tGsonHelper.builder(\"sumk.db\").registerTypeAdapter(byte[].class, ByteArrayTypeAdapter.inst).create());\n\n\tpublic static JsonOperator operator() {\n\t\treturn operator;\n\t}\n\n\tpublic static void setOperator(JsonOperator operator) {\n\t\tDBJson.operator = Objects.requireNonNull(operator);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/SDB.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.db.kit.DBKits;\nimport org.yx.db.kit.SDBuilder;\nimport org.yx.db.mapper.NamedExecutor;\nimport org.yx.db.mapper.SqlHolder;\nimport org.yx.db.sql.InsertResult;\n\npublic class SDB {\n\n\tpublic static int execute(String name, Map<String, Object> map) {\n\t\treturn NamedExecutor.execute(SqlHolder.findSql(name), map);\n\t}\n\n\tpublic static InsertResult insertWithAutoGeneratedKeys(String name, Map<String, Object> map) {\n\t\treturn NamedExecutor.insertWithAutoGeneratedKeys(SqlHolder.findSql(name), map);\n\t}\n\n\tpublic static List<Map<String, Object>> list(String name, Map<String, Object> map) {\n\t\treturn NamedExecutor.list(SqlHolder.findSql(name), map);\n\t}\n\n\tpublic static List<Object[]> listInArray(String name, Map<String, Object> map) {\n\t\treturn NamedExecutor.listInArray(SqlHolder.findSql(name), map);\n\t}\n\n\tpublic static List<?> singleColumnList(String name, Map<String, Object> map) {\n\t\treturn NamedExecutor.singleColumnList(SqlHolder.findSql(name), map);\n\t}\n\n\tpublic static long count(String name, Map<String, Object> map) {\n\t\treturn NamedExecutor.count(SqlHolder.findSql(name), map);\n\t}\n\n\tpublic static Map<String, Object> queryOne(String name, Map<String, Object> map) {\n\t\treturn DBKits.queryOne(list(name, map));\n\t}\n\n\tpublic static SDBuilder builder() {\n\t\treturn new SDBuilder();\n\t}\n\n\tpublic static SDBuilder builder(String name, Object param) {\n\t\treturn new SDBuilder().name(name).param(param);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/CommonConfigFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.yx.annotation.Bean;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\nimport org.yx.util.CollectionUtil;\n\n@Bean\npublic class CommonConfigFactory implements DBConfigFactory {\n\n\tprotected Map<String, Map<String, String>> parseFromAppInfo(String dbName) {\n\t\tMap<String, String> map = AppInfo.subMap(\"s.db.\" + dbName + \".\");\n\t\tif (map.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tMap<String, Object> configMap = CollectionUtil.flatMapToTree(map);\n\t\treturn pureMap(configMap);\n\t}\n\n\t@Override\n\tpublic List<DBConfig> create(String name) throws Exception {\n\t\tMap<String, Map<String, String>> maps = parseFromAppInfo(name);\n\t\tList<DBConfig> list = new ArrayList<>();\n\t\tif (maps == null || maps.isEmpty()) {\n\t\t\treturn list;\n\t\t}\n\t\tfor (String index : maps.keySet()) {\n\t\t\tlist.add(DBConfig.create(index, maps.get(index)));\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic static Map<String, Map<String, String>> pureMap(Map<String, Object> objectMap) {\n\t\tif (objectMap == null || objectMap.isEmpty()) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tfinal Map<String, Map<String, String>> ret = new HashMap<String, Map<String, String>>();\n\n\t\tfor (Entry<String, Object> entry : objectMap.entrySet()) {\n\t\t\tString catagory = entry.getKey();\n\t\t\tObject value = entry.getValue();\n\t\t\tif (!(value instanceof Map)) {\n\t\t\t\tLogs.db().info(\"{} is not valid config, value : {}\", catagory, value);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tMap<String, Object> config = (Map<String, Object>) value;\n\t\t\tMap<String, String> real = new HashMap<>();\n\t\t\tfor (Entry<String, Object> e2 : config.entrySet()) {\n\t\t\t\tString propertyName = e2.getKey();\n\t\t\t\tObject v = e2.getValue();\n\t\t\t\tif (!(v instanceof String)) {\n\t\t\t\t\tLogs.db().info(\"{}.{} is not valid config,value:{}\", catagory, propertyName, v);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\treal.put(propertyName, (String) v);\n\t\t\t}\n\t\t\tif (real.size() > 0) {\n\t\t\t\tret.put(catagory, real);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/ConnectionPool.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport org.slf4j.Logger;\nimport org.yx.base.context.ActionContext;\nimport org.yx.common.util.kit.Asserts;\nimport org.yx.db.enums.DBType;\nimport org.yx.db.enums.TxHook;\nimport org.yx.db.event.DBEvent;\nimport org.yx.db.event.EventLane;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.exception.SimpleSumkException;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\n\npublic final class ConnectionPool implements AutoCloseable {\n\n\tprivate static ThreadLocal<List<ConnectionPool>> connectionHolder = new ThreadLocal<List<ConnectionPool>>() {\n\n\t\t@Override\n\t\tprotected List<ConnectionPool> initialValue() {\n\t\t\treturn new ArrayList<>(2);\n\t\t}\n\n\t};\n\tprivate static final Logger LOGGER = Log.get(\"sumk.conn\");\n\n\tprivate final String dbName;\n\n\tprivate final DBType dbType;\n\n\tprivate final boolean autoCommit;\n\n\tprivate SumkConnection readConn;\n\tprivate SumkConnection writeConn;\n\tprivate List<SqlSessionHook> hooks;\n\tprivate final EventLane eventLane;\n\n\tpublic void addHook(TxHook type, Consumer<HookContext> r) {\n\t\tSqlSessionHook act = new SqlSessionHook(type, r);\n\t\tif (hooks == null) {\n\t\t\thooks = new ArrayList<>();\n\t\t}\n\t\tif (hooks.contains(act)) {\n\t\t\treturn;\n\t\t}\n\t\thooks.add(act);\n\t}\n\n\tpublic static ConnectionPool create(String dbName, DBType dbType, boolean autoCommit) {\n\t\tif (ActionContext.current().isTest() && !autoCommit) {\n\t\t\tif (connectionHolder.get().size() > 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tdbType = DBType.WRITE;\n\t\t}\n\t\tList<ConnectionPool> list = connectionHolder.get();\n\t\tConnectionPool context = new ConnectionPool(dbName, dbType, autoCommit);\n\t\tlist.add(0, context);\n\t\treturn context;\n\t}\n\n\tpublic static ConnectionPool createIfAbsent(String dbName, DBType dbType) {\n\t\tList<ConnectionPool> list = connectionHolder.get();\n\t\tif (list.size() > 0 && list.get(0).getDbName().equals(dbName)) {\n\n\t\t\treturn null;\n\t\t}\n\t\treturn create(dbName, dbType, false);\n\t}\n\n\tprivate ConnectionPool(String dbName, DBType dbType, boolean autoCommit) {\n\t\tthis.dbName = Objects.requireNonNull(dbName);\n\t\tthis.dbType = Objects.requireNonNull(dbType);\n\t\tthis.autoCommit = autoCommit;\n\t\tthis.eventLane = new EventLane();\n\t}\n\n\tpublic static ConnectionPool get() {\n\t\tList<ConnectionPool> list = connectionHolder.get();\n\t\tAsserts.requireTrue(list.size() > 0, \"must open transaction in box or other containers\");\n\t\tConnectionPool context = list.get(0);\n\t\treturn context;\n\t}\n\n\tpublic static int localPoolSize() {\n\t\tList<ConnectionPool> list = connectionHolder.get();\n\t\treturn list.size();\n\t}\n\n\tpublic static void clossLeakConnection() {\n\t\tList<ConnectionPool> list = connectionHolder.get();\n\t\tif (list.isEmpty()) {\n\t\t\tconnectionHolder.remove();\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tLOGGER.error(\"### connection leak:\" + list.size());\n\t\t\twhile (list.size() > 0) {\n\t\t\t\tlist.get(0).close();\n\t\t\t}\n\t\t\tconnectionHolder.remove();\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t}\n\t}\n\n\tpublic SumkConnection connection(DBType userType) throws SQLException {\n\t\tif (ActionContext.current().isTest()) {\n\t\t\treturn this.getWriteConnection();\n\t\t}\n\t\tswitch (this.dbType) {\n\t\tcase WRITE:\n\t\t\tif (userType == DBType.READONLY) {\n\t\t\t\tthrow new SimpleSumkException(5639234, \"can not open readOnly connection in write context\");\n\t\t\t}\n\t\t\treturn this.getWriteConnection();\n\t\tcase READONLY:\n\t\t\tif (userType == DBType.WRITE) {\n\t\t\t\tthrow new SimpleSumkException(5639234, \"can not open write connection in readonly context\");\n\t\t\t}\n\t\t\treturn this.getReadConnection();\n\t\tcase READ_PREFER:\n\t\t\tif (userType == DBType.WRITE) {\n\t\t\t\treturn this.getWriteConnection();\n\t\t\t}\n\t\t\treturn this.getReadConnection();\n\t\tdefault:\n\t\t\treturn this.connectionByCommand(userType);\n\t\t}\n\t}\n\n\tprivate SumkConnection connectionByCommand(DBType type) throws SQLException {\n\t\tif (type == DBType.WRITE) {\n\t\t\treturn this.getWriteConnection();\n\t\t}\n\t\tif (type == DBType.ANY) {\n\t\t\tif (this.readConn != null) {\n\t\t\t\treturn this.readConn;\n\t\t\t}\n\t\t\tif (this.writeConn != null) {\n\t\t\t\treturn this.writeConn;\n\t\t\t}\n\t\t}\n\t\treturn this.getReadConnection();\n\t}\n\n\tprivate SumkConnection getReadConnection() throws SQLException {\n\t\tif (this.readConn != null) {\n\t\t\treturn this.readConn;\n\t\t}\n\t\tSumkDataSource dataSource = DataSources.readDataSource(dbName);\n\t\tif (dataSource == null) {\n\t\t\tthrow new SumkException(124234154, dbName + \"没有可用的读数据源\");\n\t\t}\n\t\tif (this.writeConn != null && this.writeConn.dataSource == dataSource) {\n\t\t\tif (LOGGER.isTraceEnabled()) {\n\t\t\t\tLOGGER.trace(\"{}写锁分身出读锁\", this.dbName);\n\t\t\t}\n\t\t\tthis.readConn = this.writeConn.copy();\n\t\t\treturn this.readConn;\n\t\t}\n\t\tthis.readConn = dataSource.getConnection();\n\t\tif (LOGGER.isTraceEnabled()) {\n\t\t\tLOGGER.trace(\"open read connection:{}\", readConn);\n\t\t}\n\t\treturn readConn;\n\t}\n\n\tprivate SumkConnection getWriteConnection() throws SQLException {\n\t\tif (this.writeConn != null) {\n\t\t\treturn this.writeConn;\n\t\t}\n\t\tSumkDataSource dataSource = DataSources.writeDataSource(dbName);\n\t\tif (dataSource == null) {\n\t\t\tthrow new SumkException(124234153, dbName + \"没有可用的写数据源\");\n\t\t}\n\t\tif (this.readConn != null && this.readConn.dataSource == dataSource) {\n\t\t\tthis.writeConn = this.readConn.copy();\n\t\t\tif (LOGGER.isTraceEnabled()) {\n\t\t\t\tLOGGER.trace(\"{}读锁升级出写锁\", this.dbName);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.writeConn = dataSource.getConnection();\n\t\t\tif (LOGGER.isTraceEnabled()) {\n\t\t\t\tLOGGER.trace(\"open write connection:{}\", writeConn);\n\t\t\t}\n\t\t}\n\t\tthis.writeConn.setAutoCommit(this.autoCommit);\n\t\treturn writeConn;\n\t}\n\n\tprivate SumkDataSource getDataSource(SumkConnection c) {\n\t\tif (c == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn c.dataSource;\n\t}\n\n\tprivate void runHook(TxHook type, Throwable exception) {\n\t\tHookContext c = null;\n\t\tfor (SqlSessionHook act : this.hooks) {\n\t\t\tif (act.getType() == type) {\n\t\t\t\tif (c == null) {\n\t\t\t\t\tc = new HookContext(getDataSource(this.writeConn), getDataSource(this.readConn), exception);\n\t\t\t\t}\n\t\t\t\tact.getAction().accept(c);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void commit() throws SQLException {\n\t\tif (this.hooks != null) {\n\t\t\trunHook(TxHook.ON_COMMIT, null);\n\t\t}\n\t\tif (this.writeConn != null) {\n\t\t\tif (LOGGER.isTraceEnabled()) {\n\t\t\t\tLOGGER.trace(\"commit {}\", this.dbName);\n\t\t\t}\n\t\t\tthis.eventLane.commit(this.writeConn);\n\t\t}\n\t\tif (this.hooks != null) {\n\t\t\trunHook(TxHook.COMMITED, null);\n\t\t}\n\t}\n\n\tpublic void rollback(Throwable e) throws SQLException {\n\t\teventLane.clear();\n\t\tif (this.writeConn != null) {\n\t\t\tif (LOGGER.isTraceEnabled()) {\n\t\t\t\tLOGGER.trace(\"rollback {}\", this.dbName);\n\t\t\t}\n\t\t\tthis.writeConn.rollback();\n\t\t}\n\t\tif (this.hooks != null) {\n\t\t\trunHook(TxHook.ROLLBACK, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() throws Exception {\n\t\teventLane.clear();\n\t\tif (this.writeConn != null) {\n\t\t\ttry {\n\t\t\t\tthis.writeConn.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.printStack(\"sumk.sql.error\", e);\n\t\t\t}\n\t\t}\n\t\tif (this.readConn != null) {\n\t\t\ttry {\n\t\t\t\tthis.readConn.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.printStack(\"sumk.sql.error\", e);\n\t\t\t}\n\t\t}\n\t\tif (this.hooks != null) {\n\t\t\trunHook(TxHook.CLOSED, null);\n\t\t}\n\t\tthis.writeConn = null;\n\t\tthis.readConn = null;\n\t\tList<ConnectionPool> list = connectionHolder.get();\n\t\tint size = list.size();\n\t\tlist.remove(this);\n\t\tif (LOGGER.isTraceEnabled()) {\n\t\t\tLOGGER.trace(\"Close connection context [{}], from size {} to {}\", this.dbName, size, list.size());\n\t\t}\n\t}\n\n\tpublic String getDbName() {\n\t\treturn dbName;\n\t}\n\n\tpublic DBType getDbType() {\n\t\treturn dbType;\n\t}\n\n\tpublic boolean isAutoCommit() {\n\t\treturn autoCommit;\n\t}\n\n\tpublic void pubuishModify(DBEvent event) {\n\t\tif (this.writeConn != null) {\n\t\t\tthis.eventLane.pubuishModify(this.writeConn, event);\n\t\t}\n\t}\n\n\tpublic boolean existModifyEvent(PojoMeta pm) {\n\t\treturn this.eventLane.existModifyEvent(pm.getTableName());\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/DBCPDataSourceFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport javax.sql.DataSource;\n\nimport org.apache.commons.dbcp2.BasicDataSource;\nimport org.yx.common.util.kit.Asserts;\nimport org.yx.conf.SimpleBeanUtil;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\n\npublic class DBCPDataSourceFactory implements DataSourceFactory {\n\n\tprivate static final Map<String, String> DEFAULT_PROPERTIES = new HashMap<>();\n\n\tstatic {\n\t\tDEFAULT_PROPERTIES.put(\"driverClassName\", \"com.mysql.jdbc.Driver\");\n\n\t\tDEFAULT_PROPERTIES.put(\"maxTotal\", \"50\");\n\t\tDEFAULT_PROPERTIES.put(\"minIdle\", \"5\");\n\t\tDEFAULT_PROPERTIES.put(\"maxIdle\", \"30\");\n\t\tDEFAULT_PROPERTIES.put(\"maxWaitMillis\", \"10000\");\n\t\tDEFAULT_PROPERTIES.put(\"testOnBorrow\", \"false\");\n\t\tDEFAULT_PROPERTIES.put(\"testOnReturn\", \"false\");\n\t\tDEFAULT_PROPERTIES.put(\"testWhileIdle\", \"true\");\n\t\tDEFAULT_PROPERTIES.put(\"removeAbandonedOnBorrow\", \"false\");\n\t\tDEFAULT_PROPERTIES.put(\"removeAbandonedOnMaintenance\", \"true\");\n\t\tDEFAULT_PROPERTIES.put(\"removeAbandonedTimeout\", \"300\");\n\t\tDEFAULT_PROPERTIES.put(\"logAbandoned\", \"true\");\n\t\tDEFAULT_PROPERTIES.put(\"timeBetweenEvictionRunsMillis\", \"30000\");\n\t\tDEFAULT_PROPERTIES.put(\"softMinEvictableIdleTimeMillis\", \"60000\");\n\n\t\tDEFAULT_PROPERTIES.put(\"logExpiredConnections\", \"false\");\n\t\tDEFAULT_PROPERTIES.put(\"poolPreparedStatements\", \"false\");\n\n\t}\n\n\tprivate boolean valid(Map<String, String> properties) {\n\t\treturn properties.get(\"url\") != null && properties.get(\"username\") != null\n\t\t\t\t&& properties.get(\"password\") != null;\n\t}\n\n\t@Override\n\tpublic DataSource create(Map<String, String> properties, boolean readonly) {\n\t\tAsserts.requireTrue(this.valid(properties), \"url,username,password should not be null\");\n\t\tBasicDataSource basic = new BasicDataSource();\n\t\ttry {\n\t\t\tMap<String, String> map = new HashMap<>(DEFAULT_PROPERTIES);\n\t\t\tif (properties != null && properties.size() > 0) {\n\t\t\t\tmap.putAll(properties);\n\t\t\t}\n\t\t\tSimpleBeanUtil.copyProperties(basic, map);\n\t\t\tbasic.setDefaultReadOnly(readonly);\n\t\t} catch (Exception e) {\n\t\t\tLogs.db().error(e.getMessage(), e);\n\t\t\tthrow new SumkException(23434, e.getMessage(), e);\n\t\t}\n\t\treturn basic;\n\t}\n\n\t@Override\n\tpublic Map<String, Number> status(final DataSource datasource) {\n\t\tBasicDataSource ds = null;\n\t\tif (datasource instanceof BasicDataSource) {\n\t\t\tds = (BasicDataSource) datasource;\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tds = datasource.unwrap(BasicDataSource.class);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLogs.db().error(e.getLocalizedMessage(), e);\n\t\t\t}\n\t\t}\n\t\tif (ds == null) {\n\t\t\tLogs.db().info(\"ds.class({}) is not instance from BasicDataSource\", datasource.getClass().getName());\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\n\t\tMap<String, Number> map = new LinkedHashMap<>();\n\t\tmap.put(\"active\", ds.getNumActive());\n\t\tmap.put(\"idle\", ds.getNumIdle());\n\t\tmap.put(\"minIdle\", ds.getMinIdle());\n\t\tmap.put(\"maxIdle\", ds.getMaxIdle());\n\t\tmap.put(\"maxTotal\", ds.getMaxTotal());\n\t\treturn map;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/DBConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.db.enums.DBType;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\n\npublic class DBConfig {\n\n\tprivate static Function<String, String> passwordDecryptor = text -> {\n\t\tbyte[] bs = S.base64().decode(text.getBytes());\n\t\ttry {\n\t\t\treturn new String(S.cipher().decrypt(bs, DBSettings.getPasswordKey()));\n\t\t} catch (Exception e) {\n\t\t\tLogs.db().error(\"decrypt failed: \" + text, e);\n\t\t\tthrow new SumkException(2354223, e.getMessage(), e);\n\t\t}\n\t};\n\n\tpublic static void setPasswordDecryptor(Function<String, String> passwordDecryptor) {\n\t\tDBConfig.passwordDecryptor = Objects.requireNonNull(passwordDecryptor);\n\t}\n\n\tpublic static DBConfig create(String index, Map<String, String> p) throws Exception {\n\t\tDBType type = DBType.ANY;\n\t\tint weight = 0, readWeight = 0;\n\t\tMap<String, String> properties = new HashMap<>();\n\n\t\tSet<String> set = p.keySet();\n\t\tfor (String key : set) {\n\t\t\tString v = p.get(key);\n\t\t\tif (v == null || v.isEmpty()) {\n\t\t\t\tLogs.db().debug(\"db config {}={} is empty,ignore it.\", key, v);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tswitch (key.toLowerCase()) {\n\t\t\tcase \"type\":\n\t\t\t\ttype = DBConfig.parseFromConfigFile(v);\n\t\t\t\tbreak;\n\t\t\tcase \"weight\":\n\t\t\t\tweight = Integer.parseInt(v);\n\t\t\t\tbreak;\n\t\t\tcase \"password\":\n\n\t\t\t\tif (AppInfo.getBoolean(\"sumk.db.password.encry\", false)) {\n\t\t\t\t\tv = passwordDecryptor.apply(v);\n\t\t\t\t}\n\t\t\t\tproperties.put(key, v);\n\t\t\t\tbreak;\n\t\t\tcase \"read_weight\":\n\t\t\tcase \"readweight\":\n\t\t\t\treadWeight = Integer.parseInt(v);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tproperties.put(key, v);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn new DBConfig(index, type, weight, readWeight, properties);\n\t}\n\n\tprivate static DBType parseFromConfigFile(String type) {\n\t\tString type2 = type.toLowerCase();\n\t\tswitch (type2) {\n\t\tcase \"w\":\n\t\tcase \"write\":\n\t\t\treturn DBType.WRITE;\n\t\tcase \"r\":\n\t\tcase \"read\":\n\t\t\treturn DBType.READONLY;\n\t\tcase \"wr\":\n\t\tcase \"rw\":\n\t\tcase \"any\":\n\t\t\treturn DBType.ANY;\n\t\tdefault:\n\t\t\tthrow new SumkException(2342312, type + \" is not correct db type\");\n\t\t}\n\t}\n\n\tfinal DBType type;\n\tfinal int weight;\n\tfinal int readWeight;\n\tfinal String index;\n\tfinal Map<String, String> properties;\n\n\tpublic DBConfig(String index, DBType type, int weight, int readWeight, Map<String, String> properties) {\n\t\tthis.index = index;\n\t\tthis.type = type;\n\t\tthis.weight = weight;\n\t\tthis.readWeight = readWeight;\n\t\tthis.properties = properties;\n\t}\n\n\tpublic String getProperty(String name) {\n\t\treturn this.properties.get(name);\n\t}\n\n\tpublic Map<String, String> getProperties() {\n\t\treturn properties;\n\t}\n\n\tpublic String getIndex() {\n\t\treturn index;\n\t}\n\n\tpublic DBType getType() {\n\t\treturn type;\n\t}\n\n\tpublic int getWeight() {\n\t\treturn weight;\n\t}\n\n\tpublic int getReadWeight() {\n\t\treturn readWeight;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DBConfig [type=\" + type + \", weight=\" + weight + \", readWeight=\" + readWeight + \", index=\" + index\n\t\t\t\t+ \", properties=\" + properties + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/DBConfigFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.List;\n\nimport org.yx.base.Ordered;\n\npublic interface DBConfigFactory extends Ordered {\n\n\tList<DBConfig> create(String dbName) throws Exception;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/DSFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.sql.DataSource;\n\nimport org.slf4j.Logger;\nimport org.yx.db.enums.DBType;\nimport org.yx.log.Logs;\n\npublic final class DSFactory {\n\tprivate static DataSourceFactory factory;\n\tstatic {\n\t\ttry {\n\n\t\t\tfactory = new DBCPDataSourceFactory();\n\t\t} catch (Throwable e) {\n\t\t\tLogger log = Logs.db();\n\t\t\tif (log.isInfoEnabled()) {\n\t\t\t\tlog.error(\"cannot create dbcp2 factory,\" + e, e);\n\t\t\t} else {\n\t\t\t\tlog.error(\"cannot create dbcp2 factory\");\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static DataSourceFactory factory() {\n\t\treturn factory;\n\t}\n\n\tpublic static void setFactory(DataSourceFactory factory) {\n\t\tDSFactory.factory = Objects.requireNonNull(factory);\n\t}\n\n\tpublic static SumkDataSource create(String name, String index, DBType type, Map<String, String> properties) {\n\t\tDataSource basic = factory.create(properties, !type.isWritable());\n\t\tLogs.db().debug(\"create ds: {}\", basic);\n\t\treturn new SumkDataSource(name, index, type, basic);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/DataSourceFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.Map;\n\nimport javax.sql.DataSource;\n\npublic interface DataSourceFactory {\n\tDataSource create(Map<String, String> properties, boolean readonly);\n\n\tMap<String, Number> status(DataSource datasource);\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/DataSourceManager.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.Set;\n\npublic interface DataSourceManager {\n\n\tString status();\n\n\t/**\n\t * 销毁mananger，不能抛出异常\n\t */\n\tvoid destroy();\n\n\tSumkDataSource writeDataSource();\n\n\tSumkDataSource readDataSource();\n\n\tSet<SumkDataSource> allDataSources();\n}"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/DataSourceManagerImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeMap;\n\nimport org.yx.bean.IOC;\nimport org.yx.common.route.Router;\nimport org.yx.common.util.S;\nimport org.yx.conf.Const;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\n\npublic final class DataSourceManagerImpl implements DataSourceManager {\n\n\tprivate Router<SumkDataSource> read;\n\tprivate Router<SumkDataSource> write;\n\n\tprivate final String db;\n\n\tpublic DataSourceManagerImpl(String dbName) {\n\t\tthis.db = Objects.requireNonNull(dbName);\n\t\ttry {\n\t\t\tthis.parseDatasource();\n\t\t} catch (Exception e) {\n\t\t\tLogs.db().error(e.getMessage(), e);\n\t\t\tthrow new SumkException(1432543, dbName + \"创建DataSourceManager\" + \"失败\");\n\t\t}\n\t}\n\n\tpublic DataSourceManagerImpl(String dbName, Router<SumkDataSource> write, Router<SumkDataSource> read)\n\t\t\tthrows Exception {\n\t\tthis.db = Objects.requireNonNull(dbName);\n\t\tthis.write = write;\n\t\tthis.read = read;\n\t}\n\n\t@Override\n\tpublic String status() {\n\t\tMap<String, SumkDataSource> map = new TreeMap<>();\n\t\tfor (SumkDataSource datasource : this.allDataSources()) {\n\t\t\tmap.put(datasource.toString(), datasource);\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(100);\n\t\tfor (SumkDataSource datasource : map.values()) {\n\t\t\tsb.append(datasource).append(\" = \").append(S.json().toJson(datasource.status())).append(Const.LN);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\tfor (SumkDataSource ds : this.allDataSources()) {\n\t\t\tds.close();\n\t\t}\n\t}\n\n\tprivate void parseDatasource() throws Exception {\n\t\tif (this.write != null || this.read != null) {\n\t\t\tLogs.db().info(\"{} has inited datasource\", this.db);\n\t\t\treturn;\n\t\t}\n\t\tRouterFactory factory = IOC.get(RouterFactory.class);\n\t\tif (factory == null) {\n\t\t\tfactory = new WeightedRouterFactory();\n\t\t}\n\t\tList<Router<SumkDataSource>> container = factory.create(this.db);\n\t\tthis.write = container.get(0);\n\t\tthis.read = container.get(1);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"datasource[\" + db + \"] write=\" + write + \" ,read=\" + read;\n\t}\n\n\t@Override\n\tpublic SumkDataSource writeDataSource() {\n\t\treturn this.write.select();\n\t}\n\n\t@Override\n\tpublic SumkDataSource readDataSource() {\n\t\treturn this.read.select();\n\t}\n\n\t@Override\n\tpublic Set<SumkDataSource> allDataSources() {\n\t\tSet<SumkDataSource> set = new HashSet<>();\n\t\tset.addAll(this.read.allSources());\n\t\tset.addAll(this.write.allSources());\n\t\treturn set;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/DataSourceManagerSelector.java",
    "content": "package org.yx.db.conn;\n\nimport java.util.Set;\n\npublic interface DataSourceManagerSelector {\n\n\tDataSourceManager select(String dbName);\n\n\t/**\n\t * 已经加载的数据源名称\n\t * \n\t * @return 数据源名称列表，不为null\n\t */\n\tSet<String> dbNames();\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/DataSources.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.Objects;\n\npublic final class DataSources {\n\n\tprivate static DataSourceManagerSelector managerSelector = new DefaultManagerSelector();\n\n\tpublic static void setManagerSelector(DataSourceManagerSelector selector) {\n\t\tDataSources.managerSelector = Objects.requireNonNull(selector);\n\t}\n\n\tpublic static DataSourceManagerSelector getManagerSelector() {\n\t\treturn managerSelector;\n\t}\n\n\tpublic static SumkDataSource writeDataSource(String dbName) {\n\t\treturn getManager(dbName).writeDataSource();\n\t}\n\n\tpublic static SumkDataSource readDataSource(String dbName) {\n\t\treturn getManager(dbName).readDataSource();\n\t}\n\n\tpublic static DataSourceManager getManager(String dbName) {\n\t\treturn managerSelector.select(dbName);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/DefaultManagerSelector.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.util.StringUtil;\n\npublic class DefaultManagerSelector implements DataSourceManagerSelector {\n\n\t/**\n\t * factoryMap只能重新赋值，不能修改\n\t */\n\tprivate Map<String, DataSourceManager> factoryMap = Collections.emptyMap();\n\n\tprotected Map<String, String> aliasMap;\n\n\tprivate Function<String, DataSourceManager> managerFactory = DataSourceManagerImpl::new;\n\n\tpublic DefaultManagerSelector() {\n\t\tMap<String, String> configMap = AppInfo.subMap(\"s.alias.db.\");\n\t\tif (configMap.size() > 0) {\n\t\t\tMap<String, String> temp = new HashMap<>();\n\t\t\tfor (Entry<String, String> entry : configMap.entrySet()) {\n\t\t\t\tString name = entry.getKey();\n\t\t\t\tString value = entry.getValue();\n\t\t\t\tif (name.isEmpty() || StringUtil.isEmpty(value)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tList<String> aliases = StringUtil.splitAndTrim(StringUtil.toLatin(value), Const.COMMA);\n\t\t\t\tfor (String alias : aliases) {\n\t\t\t\t\tif (temp.putIfAbsent(alias, name) != null) {\n\t\t\t\t\t\tLogs.redis().error(\"redis别名{}重复了\", name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (temp.size() > 0) {\n\t\t\t\tthis.aliasMap = new HashMap<>(temp);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setManagerFactory(Function<String, DataSourceManager> managerFactory) {\n\t\tthis.managerFactory = Objects.requireNonNull(managerFactory);\n\t}\n\n\tpublic synchronized DataSourceManager addManagerIfAbsent(String dbName, DataSourceManager factory) {\n\t\tMap<String, DataSourceManager> map = new HashMap<>(this.factoryMap);\n\t\tDataSourceManager pre = map.putIfAbsent(dbName, factory);\n\t\tif (pre == null) {\n\t\t\tthis.factoryMap = map;\n\t\t}\n\t\treturn pre;\n\t}\n\n\tpublic synchronized DataSourceManager removeManager(String dbName) throws Exception {\n\t\tMap<String, DataSourceManager> map = new HashMap<>(this.factoryMap);\n\t\tDataSourceManager old = map.remove(dbName);\n\t\tif (old != null) {\n\t\t\tthis.factoryMap = map;\n\t\t}\n\t\treturn old;\n\t}\n\n\tprotected String getDbName(String dbName) {\n\t\tif (this.aliasMap == null) {\n\t\t\treturn dbName;\n\t\t}\n\t\treturn aliasMap.getOrDefault(dbName, dbName);\n\t}\n\n\t@Override\n\tpublic DataSourceManager select(String dbName) {\n\t\tdbName = getDbName(dbName);\n\t\tDataSourceManager factory = factoryMap.get(dbName);\n\t\tif (factory != null) {\n\t\t\treturn factory;\n\t\t}\n\t\ttry {\n\t\t\tfor (int i = 0; i < 3; i++) {\n\t\t\t\tsynchronized (this) {\n\t\t\t\t\tif ((factory = factoryMap.get(dbName)) != null) {\n\t\t\t\t\t\treturn factory;\n\t\t\t\t\t}\n\t\t\t\t\tfactory = managerFactory.apply(dbName);\n\t\t\t\t\tif (factory == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tLogs.db().info(\"create dataSource manager: {}\", dbName);\n\t\t\t\t\tDataSourceManager pre = this.addManagerIfAbsent(dbName, factory);\n\t\t\t\t\tif (pre == null) {\n\t\t\t\t\t\treturn factory;\n\t\t\t\t\t}\n\t\t\t\t\tLogs.db().info(\"create dataSource manager '{}' twice!!\", dbName);\n\t\t\t\t\tfactory.destroy();\n\t\t\t\t\treturn pre;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new SumkException(100234321, \"get DataSourceManager [\" + dbName + \"] failed\");\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(100234325, \"create DataSourceManager [\" + dbName + \"] failed\", e);\n\t\t}\n\t}\n\n\tpublic Map<String, String> aliasMap() {\n\t\treturn new HashMap<>(aliasMap);\n\t}\n\n\tpublic Set<String> dbNames() {\n\t\treturn new HashSet<>(this.factoryMap.keySet());\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/HookContext.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\n/**\n * 所有字段都有可能为null\n */\npublic class HookContext {\n\tprivate final SumkDataSource write;\n\tprivate final SumkDataSource read;\n\tprivate final Throwable exception;\n\n\tpublic HookContext(SumkDataSource write, SumkDataSource read, Throwable exception) {\n\t\tthis.write = write;\n\t\tthis.read = read;\n\t\tthis.exception = exception;\n\t}\n\n\tpublic SumkDataSource getWrite() {\n\t\treturn write;\n\t}\n\n\tpublic SumkDataSource getRead() {\n\t\treturn read;\n\t}\n\n\tpublic Throwable getException() {\n\t\treturn exception;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/RouterFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.List;\n\nimport org.yx.common.route.Router;\n\npublic interface RouterFactory {\n\n\tList<Router<SumkDataSource>> create(String dbName) throws Exception;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/SqlSessionHook.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport org.yx.db.enums.TxHook;\n\nclass SqlSessionHook {\n\tprivate final TxHook type;\n\tprivate final Consumer<HookContext> action;\n\n\tpublic SqlSessionHook(TxHook type, Consumer<HookContext> action) {\n\t\tthis.type = Objects.requireNonNull(type);\n\t\tthis.action = Objects.requireNonNull(action);\n\t}\n\n\tpublic TxHook getType() {\n\t\treturn type;\n\t}\n\n\tpublic Consumer<HookContext> getAction() {\n\t\treturn action;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((action == null) ? 0 : action.hashCode());\n\t\tresult = prime * result + ((type == null) ? 0 : type.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tSqlSessionHook other = (SqlSessionHook) obj;\n\t\tif (action == null) {\n\t\t\tif (other.action != null)\n\t\t\t\treturn false;\n\t\t} else if (!action.equals(other.action))\n\t\t\treturn false;\n\t\tif (type != other.type)\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/SumkConnection.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.sql.Array;\nimport java.sql.Blob;\nimport java.sql.CallableStatement;\nimport java.sql.Clob;\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.NClob;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLClientInfoException;\nimport java.sql.SQLException;\nimport java.sql.SQLWarning;\nimport java.sql.SQLXML;\nimport java.sql.Savepoint;\nimport java.sql.Statement;\nimport java.sql.Struct;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.concurrent.Executor;\n\nimport org.yx.base.context.ActionContext;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\n\npublic final class SumkConnection implements Connection {\n\n\tprivate Connection inner;\n\n\tfinal SumkDataSource dataSource;\n\n\tprivate final boolean originAutoCommit;\n\tprivate boolean autoCommit;\n\n\t@Override\n\tpublic boolean isReadOnly() throws SQLException {\n\t\treturn inner.isReadOnly();\n\t}\n\n\tpublic SumkConnection(Connection inner, SumkDataSource ds) {\n\t\tthis.inner = inner;\n\t\tthis.dataSource = ds;\n\t\tthis.originAutoCommit = originAutoCommit();\n\t}\n\n\tprivate boolean originAutoCommit() {\n\t\ttry {\n\t\t\tthis.autoCommit = inner.getAutoCommit();\n\t\t\treturn this.autoCommit;\n\t\t} catch (SQLException e) {\n\t\t\tLogs.db().error(\"获取原始的autoCommit失败\", e);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tpublic SumkDataSource dataSource() {\n\t\treturn dataSource;\n\t}\n\n\t@Override\n\tpublic <T> T unwrap(Class<T> iface) throws SQLException {\n\t\treturn inner.unwrap(iface);\n\t}\n\n\t@Override\n\tpublic boolean isWrapperFor(Class<?> iface) throws SQLException {\n\t\treturn inner.isWrapperFor(iface);\n\t}\n\n\t@Override\n\tpublic Statement createStatement() throws SQLException {\n\t\tStatement stmt = inner.createStatement();\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql) throws SQLException {\n\t\tPreparedStatement stmt = inner.prepareStatement(sql);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic CallableStatement prepareCall(String sql) throws SQLException {\n\t\tCallableStatement stmt = inner.prepareCall(sql);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic String nativeSQL(String sql) throws SQLException {\n\t\treturn inner.nativeSQL(sql);\n\t}\n\n\t@Override\n\tpublic void setAutoCommit(boolean auto) throws SQLException {\n\t\tif (auto == this.autoCommit) {\n\t\t\treturn;\n\t\t}\n\t\tinner.setAutoCommit(auto);\n\t\tthis.autoCommit = auto;\n\t}\n\n\t@Override\n\tpublic boolean getAutoCommit() {\n\t\treturn autoCommit;\n\t}\n\n\t@Override\n\tpublic void commit() throws SQLException {\n\t\tif (ActionContext.current().isTest()) {\n\t\t\tthis.rollback();\n\t\t\treturn;\n\t\t}\n\t\tif (inner == null) {\n\t\t\tthrow new SQLException(\"connection is closed\");\n\t\t}\n\t\tinner.commit();\n\t}\n\n\t@Override\n\tpublic void rollback() throws SQLException {\n\t\tinner.rollback();\n\t}\n\n\t@Override\n\tpublic void close() throws SQLException {\n\t\tif (this.inner == null || this.inner.isClosed()) {\n\t\t\treturn;\n\t\t}\n\t\tif (Log.get(\"sumk.conn\").isTraceEnabled()) {\n\t\t\tLog.get(\"sumk.conn\").trace(\"close connection: \" + this);\n\t\t}\n\t\ttry {\n\t\t\tthis.recoverAutoCommit();\n\t\t} catch (Exception e) {\n\t\t\tLog.get(\"sumk.conn\").error(e.getMessage(), e);\n\t\t}\n\t\tinner.close();\n\t\tthis.inner = null;\n\t}\n\n\t@Override\n\tpublic boolean isClosed() throws SQLException {\n\t\tif (this.inner == null) {\n\t\t\treturn true;\n\t\t}\n\t\treturn inner.isClosed();\n\t}\n\n\t@Override\n\tpublic DatabaseMetaData getMetaData() throws SQLException {\n\t\treturn inner.getMetaData();\n\t}\n\n\t@Override\n\tpublic void setReadOnly(boolean readOnly) throws SQLException {\n\t\tinner.setReadOnly(readOnly);\n\t}\n\n\t@Override\n\tpublic void setCatalog(String catalog) throws SQLException {\n\t\tinner.setCatalog(catalog);\n\t}\n\n\t@Override\n\tpublic String getCatalog() throws SQLException {\n\t\treturn inner.getCatalog();\n\t}\n\n\t@Override\n\tpublic void setTransactionIsolation(int level) throws SQLException {\n\t\tinner.setTransactionIsolation(level);\n\t}\n\n\t@Override\n\tpublic int getTransactionIsolation() throws SQLException {\n\t\treturn inner.getTransactionIsolation();\n\t}\n\n\t@Override\n\tpublic SQLWarning getWarnings() throws SQLException {\n\t\treturn inner.getWarnings();\n\t}\n\n\t@Override\n\tpublic void clearWarnings() throws SQLException {\n\t\tinner.clearWarnings();\n\t}\n\n\t@Override\n\tpublic Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {\n\t\tStatement stmt = inner.createStatement(resultSetType, resultSetConcurrency);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)\n\t\t\tthrows SQLException {\n\t\tPreparedStatement stmt = inner.prepareStatement(sql, resultSetType, resultSetConcurrency);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {\n\t\tCallableStatement stmt = inner.prepareCall(sql, resultSetType, resultSetConcurrency);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic Map<String, Class<?>> getTypeMap() throws SQLException {\n\t\treturn inner.getTypeMap();\n\t}\n\n\t@Override\n\tpublic void setTypeMap(Map<String, Class<?>> map) throws SQLException {\n\t\tinner.setTypeMap(map);\n\t}\n\n\t@Override\n\tpublic void setHoldability(int holdability) throws SQLException {\n\t\tinner.setHoldability(holdability);\n\t}\n\n\t@Override\n\tpublic int getHoldability() throws SQLException {\n\t\treturn inner.getHoldability();\n\t}\n\n\t@Override\n\tpublic Savepoint setSavepoint() throws SQLException {\n\t\treturn inner.setSavepoint();\n\t}\n\n\t@Override\n\tpublic Savepoint setSavepoint(String name) throws SQLException {\n\t\treturn inner.setSavepoint(name);\n\t}\n\n\t@Override\n\tpublic void rollback(Savepoint savepoint) throws SQLException {\n\t\tinner.rollback(savepoint);\n\t}\n\n\t@Override\n\tpublic void releaseSavepoint(Savepoint savepoint) throws SQLException {\n\t\tinner.releaseSavepoint(savepoint);\n\t}\n\n\t@Override\n\tpublic Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)\n\t\t\tthrows SQLException {\n\t\tStatement stmt = inner.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,\n\t\t\tint resultSetHoldability) throws SQLException {\n\t\tPreparedStatement stmt = inner.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,\n\t\t\tint resultSetHoldability) throws SQLException {\n\t\tCallableStatement stmt = inner.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {\n\t\tPreparedStatement stmt = inner.prepareStatement(sql, autoGeneratedKeys);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {\n\t\tPreparedStatement stmt = inner.prepareStatement(sql, columnIndexes);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {\n\t\tPreparedStatement stmt = inner.prepareStatement(sql, columnNames);\n\t\treturn stmt;\n\t}\n\n\t@Override\n\tpublic Clob createClob() throws SQLException {\n\t\treturn inner.createClob();\n\t}\n\n\t@Override\n\tpublic Blob createBlob() throws SQLException {\n\t\treturn inner.createBlob();\n\t}\n\n\t@Override\n\tpublic NClob createNClob() throws SQLException {\n\t\treturn inner.createNClob();\n\t}\n\n\t@Override\n\tpublic SQLXML createSQLXML() throws SQLException {\n\t\treturn inner.createSQLXML();\n\t}\n\n\t@Override\n\tpublic boolean isValid(int timeout) throws SQLException {\n\t\treturn inner.isValid(timeout);\n\t}\n\n\t@Override\n\tpublic void setClientInfo(String name, String value) throws SQLClientInfoException {\n\t\tinner.setClientInfo(name, value);\n\t}\n\n\t@Override\n\tpublic void setClientInfo(Properties properties) throws SQLClientInfoException {\n\t\tinner.setClientInfo(properties);\n\t}\n\n\t@Override\n\tpublic String getClientInfo(String name) throws SQLException {\n\t\treturn inner.getClientInfo(name);\n\t}\n\n\t@Override\n\tpublic Properties getClientInfo() throws SQLException {\n\t\treturn inner.getClientInfo();\n\t}\n\n\t@Override\n\tpublic Array createArrayOf(String typeName, Object[] elements) throws SQLException {\n\t\treturn inner.createArrayOf(typeName, elements);\n\t}\n\n\t@Override\n\tpublic Struct createStruct(String typeName, Object[] attributes) throws SQLException {\n\t\treturn inner.createStruct(typeName, attributes);\n\t}\n\n\t@Override\n\tpublic void setSchema(String schema) throws SQLException {\n\t\tinner.setSchema(schema);\n\t}\n\n\t@Override\n\tpublic String getSchema() throws SQLException {\n\t\treturn inner.getSchema();\n\t}\n\n\t@Override\n\tpublic void abort(Executor executor) throws SQLException {\n\t\tinner.abort(executor);\n\t}\n\n\t@Override\n\tpublic void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {\n\t\tinner.setNetworkTimeout(executor, milliseconds);\n\t}\n\n\t@Override\n\tpublic int getNetworkTimeout() throws SQLException {\n\t\treturn inner.getNetworkTimeout();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.dataSource + \"::\" + String.valueOf(inner);\n\t}\n\n\tpublic boolean isSameInnerConnection(SumkConnection w) {\n\t\treturn w != null && this.inner == w.inner && this.dataSource == w.dataSource;\n\t}\n\n\tpublic SumkConnection copy() {\n\t\treturn new SumkConnection(this.inner, this.dataSource);\n\t}\n\n\tprivate void recoverAutoCommit() throws SQLException {\n\n\t\tif (originAutoCommit == this.getAutoCommit()) {\n\t\t\treturn;\n\t\t}\n\t\tLog.get(\"sumk.conn\").trace(\"recover autoCommit to {}\", originAutoCommit);\n\t\tinner.setAutoCommit(originAutoCommit);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/SumkDataSource.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.io.PrintWriter;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.SQLFeatureNotSupportedException;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.logging.Logger;\n\nimport javax.sql.DataSource;\n\nimport org.yx.db.enums.DBType;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\n\npublic class SumkDataSource implements DataSource {\n\tprivate final String name;\n\tprivate final DBType type;\n\tprivate final DataSource proxy;\n\tprivate final String index;\n\tprivate boolean enable = true;\n\tprivate final AtomicLong openCounter = new AtomicLong();\n\n\tpublic SumkDataSource(String name, String index, DBType type, DataSource ds) {\n\t\tthis.name = Objects.requireNonNull(name);\n\t\tthis.type = Objects.requireNonNull(type);\n\t\tthis.proxy = Objects.requireNonNull(ds);\n\t\tthis.index = index;\n\t}\n\n\tpublic long getOpenCount() {\n\t\treturn openCounter.get();\n\t}\n\n\tpublic String getIndex() {\n\t\treturn index;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic DBType getType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic SumkConnection getConnection() throws SQLException {\n\t\tthis.openCounter.incrementAndGet();\n\t\treturn new SumkConnection(proxy.getConnection(), this);\n\t}\n\n\t@Override\n\tpublic PrintWriter getLogWriter() throws SQLException {\n\t\treturn proxy.getLogWriter();\n\t}\n\n\t@Override\n\tpublic void setLogWriter(PrintWriter out) throws SQLException {\n\t\tproxy.setLogWriter(out);\n\t}\n\n\t@Override\n\tpublic void setLoginTimeout(int seconds) throws SQLException {\n\t\tproxy.setLoginTimeout(seconds);\n\t}\n\n\t@Override\n\tpublic int getLoginTimeout() throws SQLException {\n\t\treturn proxy.getLoginTimeout();\n\t}\n\n\t@Override\n\tpublic Logger getParentLogger() throws SQLFeatureNotSupportedException {\n\t\treturn proxy.getParentLogger();\n\t}\n\n\t@Override\n\tpublic <T> T unwrap(Class<T> iface) throws SQLException {\n\t\tif (iface == this.getClass()) {\n\t\t\treturn iface.cast(this);\n\t\t}\n\t\tif (iface.isInstance(proxy)) {\n\t\t\treturn iface.cast(proxy);\n\t\t}\n\t\tthrow new SumkException(234345, this.getClass().getSimpleName() + \" does not wrap \" + iface.getName());\n\t}\n\n\t@Override\n\tpublic boolean isWrapperFor(Class<?> iface) throws SQLException {\n\t\treturn iface == this.getClass() || iface.isInstance(proxy);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new StringBuilder().append(name).append(\".\").append(index).toString();\n\t}\n\n\t@Override\n\tpublic Connection getConnection(String username, String password) throws SQLException {\n\t\tthis.openCounter.incrementAndGet();\n\t\treturn new SumkConnection(proxy.getConnection(username, password), this);\n\t}\n\n\tpublic boolean isEnable() {\n\t\treturn enable;\n\t}\n\n\tpublic void setEnable(boolean enable) {\n\t\tthis.enable = enable;\n\t}\n\n\tpublic boolean close() {\n\t\tif (!(this.proxy instanceof AutoCloseable)) {\n\t\t\tLogs.db().warn(\"DataSource {} unsupport close\", proxy);\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tAutoCloseable c = (AutoCloseable) proxy;\n\t\t\tLogs.db().info(\"DataSource {} begin closing...\", c);\n\t\t\tc.close();\n\t\t\tLogs.db().info(\"DataSource {} closed\", c);\n\t\t\treturn true;\n\t\t} catch (Throwable e) {\n\t\t\tLogs.db().error(e.getMessage(), e);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic Map<String, Number> status() {\n\t\tMap<String, Number> map = new LinkedHashMap<>();\n\t\tMap<String, Number> inner = DSFactory.factory().status(proxy);\n\t\tif (inner != null) {\n\t\t\tmap.putAll(inner);\n\t\t}\n\t\tmap.put(\"openCount\", getOpenCount());\n\t\treturn map;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/WeightedDataSource.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport org.yx.common.route.AbstractWeightedServer;\n\npublic class WeightedDataSource extends AbstractWeightedServer<SumkDataSource> {\n\n\tpublic WeightedDataSource(SumkDataSource source) {\n\t\tsuper(source);\n\t}\n\n\t@Override\n\tpublic boolean isEnable() {\n\t\treturn this.source.isEnable();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/conn/WeightedRouterFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.conn;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.yx.bean.IOC;\nimport org.yx.common.route.Router;\nimport org.yx.common.route.Routes;\nimport org.yx.common.route.WeightedServer;\nimport org.yx.conf.AppInfo;\nimport org.yx.db.enums.DBType;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\n\npublic class WeightedRouterFactory implements RouterFactory {\n\n\t@Override\n\tpublic List<Router<SumkDataSource>> create(String dbName) throws Exception {\n\t\tList<DBConfig> configs = parseDBConfig(dbName);\n\t\tList<WeightedServer<SumkDataSource>> readDSList = new ArrayList<>(1);\n\t\tList<WeightedServer<SumkDataSource>> writeDSList = new ArrayList<>(1);\n\n\t\tfor (DBConfig dc : configs) {\n\t\t\tSumkDataSource ds = DSFactory.create(dbName, dc.index, dc.type, dc.properties);\n\t\t\tif (ds.getType().isWritable()) {\n\t\t\t\tWeightedServer<SumkDataSource> w = new WeightedDataSource(ds);\n\t\t\t\tw.setWeight(dc.getWeight() > 0 ? dc.getWeight() : 1);\n\t\t\t\twriteDSList.add(w);\n\t\t\t\tif (ds.getType() == DBType.ANY) {\n\t\t\t\t\tWeightedServer<SumkDataSource> r = new WeightedDataSource(ds);\n\t\t\t\t\tr.setWeight(dc.getReadWeight() > 0 ? dc.getReadWeight() : 1);\n\t\t\t\t\treadDSList.add(r);\n\t\t\t\t}\n\t\t\t} else if (ds.getType().isReadable()) {\n\t\t\t\tWeightedServer<SumkDataSource> r = new WeightedDataSource(ds);\n\t\t\t\tint w = dc.getReadWeight() > 0 ? dc.getReadWeight() : dc.getWeight();\n\t\t\t\tr.setWeight(w > 0 ? w : 1);\n\t\t\t\treadDSList.add(r);\n\t\t\t}\n\t\t}\n\n\t\tRouter<SumkDataSource> read = createWeightedRouter(dbName, DBType.READ_PREFER, readDSList);\n\t\tRouter<SumkDataSource> write = createWeightedRouter(dbName, DBType.WRITE, writeDSList);\n\t\treturn Arrays.asList(write, read);\n\t}\n\n\tprotected Router<SumkDataSource> createWeightedRouter(String name, DBType type,\n\t\t\tList<WeightedServer<SumkDataSource>> wds) {\n\t\tif (wds.isEmpty()) {\n\t\t\tif (AppInfo.getBoolean(\"sumk.db.empty.allow\", false)) {\n\t\t\t\tLogs.db().warn(\"you have not config any read datasource for [{}]\", name);\n\t\t\t} else {\n\t\t\t\tthrow new SumkException(83587871, \"you have not config \" + type + \" datasource for \" + name);\n\t\t\t}\n\t\t}\n\t\treturn Routes.createWeightedRouter(wds);\n\t}\n\n\tprotected List<DBConfig> parseDBConfig(String db) throws Exception {\n\t\tList<DBConfigFactory> factorys = IOC.getBeans(DBConfigFactory.class);\n\t\tfor (DBConfigFactory factory : factorys) {\n\t\t\tList<DBConfig> configs = factory.create(db);\n\t\t\tif (configs != null) {\n\t\t\t\treturn configs;\n\t\t\t}\n\t\t}\n\t\tthrow new SumkException(83587875, \"no DBConfigFactory for \" + db);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/dao/AbstractCachable.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.dao;\n\nimport org.yx.db.sql.DBFlag;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.util.BitUtil;\n\npublic abstract class AbstractCachable {\n\n\tprivate boolean cacheEnable = true;\n\n\tpublic boolean isCacheEnable() {\n\t\treturn cacheEnable && BitUtil.getBit(DBSettings.flag(), DBFlag.SELECT_FROM_CACHE);\n\t}\n\n\tprotected void setCacheEnable(boolean cache) {\n\t\tthis.cacheEnable = cache;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/dao/CountedResult.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.dao;\n\nimport java.util.List;\n\n/**\n * 用来存放结果集以及总记录数\n */\npublic class CountedResult<T> {\n\tprivate List<T> list;\n\tprivate long count;\n\n\tpublic CountedResult(List<T> list, long count) {\n\t\tthis.list = list;\n\t\tthis.count = count;\n\t}\n\n\t/**\n\t * @return 当前页的结果集\n\t */\n\tpublic List<T> getResult() {\n\t\treturn list;\n\t}\n\n\t/**\n\t * @return 符合条件的总记录数\n\t */\n\tpublic long getCount() {\n\t\treturn count;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/enums/CacheType.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.enums;\n\npublic enum CacheType {\n\n\tNOCACHE,\n\n\tSINGLE,\n\n\tLIST;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/enums/ColumnType.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.enums;\n\n/**\n * 无论是数据库主键，还是redis主键，都不允许为null\n * \n * @author 游夏\n *\n */\npublic enum ColumnType {\n\tNORMAL(false, false, 100),\n\t/**\n\t * 数据库主键。不允许为null。在更新的时候，如果没有显式设置where条件，主键字段将不会被更新。<BR>\n\t */\n\tID_DB(true, false, 1),\n\t/**\n\t * redis 主键，不允许为null\n\t */\n\tID_CACHE(false, true, 2),\n\t/**\n\t * 既是数据库主键，也是redis主键。不允许为null，不会被更新\n\t */\n\tID_BOTH(true, true, 1);\n\n\tprivate final boolean dbID;\n\tprivate final boolean cacheID;\n\tprivate final int order;\n\n\tpublic int order() {\n\t\treturn this.order;\n\t}\n\n\tpublic boolean isDbID() {\n\t\treturn dbID;\n\t}\n\n\tpublic boolean isCacheID() {\n\t\treturn cacheID;\n\t}\n\n\tprivate ColumnType(boolean dbId, boolean cacheId, int order) {\n\t\tthis.dbID = dbId;\n\t\tthis.cacheID = cacheId;\n\t\tthis.order = order;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/enums/DBType.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.enums;\n\npublic enum DBType {\n\n\tWRITE(true, false),\n\n\tREAD_PREFER(false, true),\n\n\tREADONLY(false, true),\n\n\tANY(true, true);\n\n\tprivate boolean writable;\n\tprivate boolean readable;\n\n\tprivate DBType(boolean writable, boolean readable) {\n\t\tthis.writable = writable;\n\t\tthis.readable = readable;\n\t}\n\n\tpublic boolean isWritable() {\n\t\treturn writable;\n\t}\n\n\tpublic boolean isReadable() {\n\t\treturn readable;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/enums/TransactionType.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.enums;\n\npublic enum TransactionType {\n\t/**\n\t * 如果没有事务，就开启新事物。如果已经存在，就啥都不做\n\t */\n\tREQUIRED,\n\n\t/**\n\t * 新事务\n\t */\n\tREQUIRES_NEW,\n\n\tAUTO_COMMIT;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/enums/TxHook.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.enums;\n\n/**\n * 触发钩子的情形有以下两种：\n * <OL>\n * <LI>最外层的@Box方法执行结束，无论是否有实际操作数据库都会触发</LI>\n * <LI>直接调用DB.commit()、DB.rollback()也会触发钩子</LI>\n * </OL>\n */\npublic enum TxHook {\n\t/**\n\t * 只有这个钩子可以操作数据库，但不要做太复杂的操作。 它在Const.LISTENER_DB_MODIFY_ON_COMMIT前执行\n\t */\n\tON_COMMIT,\n\t/**\n\t * 它在Const.LISTENER_DB_MODIFY后执行\n\t */\n\tCOMMITED,\n\t/**\n\t * 回滚之后执行\n\t */\n\tROLLBACK, CLOSED\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/enums/ValidRecord.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.enums;\n\npublic enum ValidRecord {\n\t/**\n\t * 在SoftDelete中，跟VALID相同的表示有效，其它都是无效\n\t */\n\tEQUAL_VALID,\n\n\t/**\n\t * 在SoftDelete中，只要跟INVALID不同都是有效\n\t */\n\tNOT_INVALID\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/event/DBEvent.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.event;\n\nimport java.util.Objects;\n\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.db.sql.PojoMetaHolder;\n\npublic class DBEvent {\n\n\tprivate final String tableName;\n\n\tpublic DBEvent(String table) {\n\t\tthis.tableName = Objects.requireNonNull(table);\n\t}\n\n\tpublic String getTable() {\n\t\treturn this.tableName;\n\t}\n\n\tpublic PojoMeta getTableMeta() {\n\t\treturn PojoMetaHolder.getTableMeta(this.tableName);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/event/DBEventPublisher.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.event;\n\nimport java.util.List;\n\nimport org.yx.bean.IOC;\nimport org.yx.common.listener.EventBus;\nimport org.yx.conf.Const;\n\npublic final class DBEventPublisher {\n\n\tprivate static EventBus modifyBus;\n\tprivate static EventBus queryBus;\n\tprivate static EventBus onCommitBus;\n\n\tpublic static void init() {\n\t\tmodifyBus = IOC.get(Const.LISTENER_DB_MODIFY, EventBus.class);\n\t\tqueryBus = IOC.get(Const.LISTENER_DB_QUERY, EventBus.class);\n\t\tonCommitBus = IOC.get(Const.LISTENER_DB_MODIFY_ON_COMMIT, EventBus.class);\n\t}\n\n\tpublic static void onCommit(List<DBEvent> events) {\n\t\tif (onCommitBus != null) {\n\t\t\tonCommitBus.publishBatch(events);\n\t\t}\n\t}\n\n\tpublic static void publishModify(List<DBEvent> events) {\n\t\tmodifyBus.publishBatch(events);\n\t}\n\n\tpublic static void publishModify(DBEvent event) {\n\t\tmodifyBus.publish(event);\n\t}\n\n\tpublic static void publishQuery(QueryEvent event) {\n\t\tqueryBus.publish(event);\n\t}\n\n}"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/event/DeleteEvent.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.event;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic class DeleteEvent extends ModifyEvent {\n\n\tprivate List<Map<String, Object>> wheres;\n\n\tpublic DeleteEvent(String table, int flag, List<Map<String, Object>> wheres) {\n\t\tsuper(table, flag);\n\t\tthis.wheres = wheres;\n\t}\n\n\tpublic List<Map<String, Object>> getWheres() {\n\t\treturn this.wheres;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/event/EventLane.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.event;\n\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.yx.db.conn.SumkConnection;\n\npublic final class EventLane {\n\tprivate List<DBEvent> events;\n\n\tpublic EventLane() {\n\t}\n\n\tprivate List<DBEvent> makeSureEvents() {\n\t\tif (this.events == null) {\n\t\t\tthis.events = new ArrayList<>();\n\t\t}\n\t\treturn this.events;\n\t}\n\n\tpublic void pubuishModify(SumkConnection conn, DBEvent event) {\n\t\tif (event == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (!conn.getAutoCommit()) {\n\t\t\tmakeSureEvents().add(event);\n\t\t} else {\n\t\t\tDBEventPublisher.publishModify(event);\n\t\t}\n\t}\n\n\tpublic void commit(SumkConnection conn) throws SQLException {\n\t\tif (this.events == null) {\n\t\t\tconn.commit();\n\t\t\treturn;\n\t\t}\n\t\tDBEventPublisher.onCommit(events);\n\t\tconn.commit();\n\t\tDBEventPublisher.publishModify(events);\n\t\tthis.clear();\n\t}\n\n\tpublic void clear() {\n\t\tthis.events = null;\n\t}\n\n\tpublic boolean existModifyEvent(String table) {\n\t\tif (this.events == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (DBEvent event : this.events) {\n\t\t\tif (event.getTable().equals(table) && (event instanceof ModifyEvent)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/event/InsertEvent.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.event;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic class InsertEvent extends ModifyEvent {\n\n\tprivate List<Map<String, Object>> pojos;\n\n\tpublic InsertEvent(String table, int flag, List<Map<String, Object>> pojos) {\n\t\tsuper(table, flag);\n\t\tthis.pojos = pojos;\n\t}\n\n\tpublic List<Map<String, Object>> getPojos() {\n\t\treturn pojos;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/event/ModifyEvent.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.event;\n\nimport org.yx.exception.SumkException;\nimport org.yx.util.BitUtil;\n\npublic class ModifyEvent extends DBEvent {\n\tprivate int affected;\n\tprotected int flag;\n\n\tpublic ModifyEvent(String table, int flag) {\n\t\tsuper(table);\n\t\tthis.flag = flag;\n\t}\n\n\tpublic int getAffected() {\n\t\treturn affected;\n\t}\n\n\tpublic void setAffected(int modified) {\n\t\tthis.affected = modified;\n\t}\n\n\tpublic int flag() {\n\t\treturn flag;\n\t}\n\n\t/**\n\t * 用于支持event之间传递boolean值，比如是否已经被处理了。调用本方法进行设置，调用getBoolean方法获取设置的结果\n\t * 这个用于扩展，框架本身并没有用到\n\t * \n\t * @param slot 29到32之间的一个数字，只要跟getBoolean里的位数能对上就行，无实际意义\n\t * @param h    布尔值\n\t */\n\tpublic void setBoolean(int slot, boolean h) {\n\t\tif (slot < 29 || slot > 32) {\n\t\t\tthrow new SumkException(346243451, \"slot必须在29-32之间，实际却是:\" + slot);\n\t\t}\n\t\tthis.flag = BitUtil.setBit(flag, slot, h);\n\t}\n\n\tpublic boolean getBoolean(int slot) {\n\t\treturn BitUtil.getBit(flag, slot);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/event/QueryEvent.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.event;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic class QueryEvent extends DBEvent {\n\tpublic QueryEvent(String table) {\n\t\tsuper(table);\n\t}\n\n\tList<Map<String, Object>> in;\n\tList<?> result;\n\n\tpublic List<Map<String, Object>> getIn() {\n\t\treturn in;\n\t}\n\n\tpublic void setIn(List<Map<String, Object>> in) {\n\t\tthis.in = in;\n\t}\n\n\tpublic List<?> getResult() {\n\t\treturn result;\n\t}\n\n\tpublic void setResult(List<?> result) {\n\t\tthis.result = result;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/event/UpdateEvent.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.event;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.base.sumk.map.ListMap;\nimport org.yx.db.sql.DBFlag;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.util.BitUtil;\n\npublic class UpdateEvent extends ModifyEvent {\n\n\tprivate final Map<String, Object> to;\n\tprivate final Map<String, Number> incrMap;\n\tprivate final List<Map<String, Object>> wheres;\n\n\tpublic UpdateEvent(String table, int flag, Map<String, Object> to, Map<String, Number> incrMap,\n\t\t\tList<Map<String, Object>> wheres) {\n\t\tsuper(table, flag);\n\t\tthis.incrMap = incrMap == null || incrMap.isEmpty() ? null : incrMap;\n\t\tthis.wheres = wheres;\n\t\tthis.to = isFullUpdate() ? to : removeNull(to);\n\t}\n\n\tpublic boolean isFullUpdate() {\n\t\treturn BitUtil.getBit(flag, DBFlag.UPDATE_FULL_UPDATE);\n\t}\n\n\tpublic boolean isUpdateDBID() {\n\t\treturn BitUtil.getBit(flag, DBFlag.UPDATE_UPDATE_DBID);\n\t}\n\n\tpublic Map<String, Object> getTo() {\n\t\treturn to;\n\t}\n\n\tpublic List<Map<String, Object>> getWheres() {\n\t\treturn wheres;\n\t}\n\n\tpublic Map<String, Number> getIncrMap() {\n\t\treturn incrMap;\n\t}\n\n\tpublic boolean canUpdateCache() {\n\t\treturn wheres.size() == 1 && getAffected() == 1 && incrMap == null && isFullUpdate()\n\t\t\t\t&& BitUtil.getBit(DBSettings.flag(), DBFlag.UPDATE_TO_CACHE);\n\t}\n\n\tprivate <K, V> Map<K, V> removeNull(@NotNull Map<K, V> map) {\n\t\tMap<K, V> ret = new ListMap<>();\n\t\tfor (Entry<K, V> entry : map.entrySet()) {\n\t\t\tK k = entry.getKey();\n\t\t\tV v = entry.getValue();\n\t\t\tif (k == null || v == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tret.put(k, v);\n\t\t}\n\t\treturn ret;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/exec/BoxAopExecutorSupplier.java",
    "content": "package org.yx.db.exec;\n\nimport java.lang.reflect.Method;\n\nimport org.yx.annotation.Bean;\nimport org.yx.bean.aop.AopExecutor;\nimport org.yx.bean.aop.AopExecutorSupplier;\nimport org.yx.db.spec.BoxSpec;\nimport org.yx.db.spec.DBSpecs;\n\n@Bean\npublic class BoxAopExecutorSupplier implements AopExecutorSupplier {\n\n\t/**\n\t * 事务比一般的aop早启动，这样大多数的aop都在事务里执行\n\t */\n\t@Override\n\tpublic int order() {\n\t\treturn 50;\n\t}\n\n\tprivate DBSource[] boxs = new DBSource[0];\n\n\tprivate DBSource changeToDBSource(BoxSpec box) {\n\t\tfor (DBSource tmp : boxs) {\n\t\t\tif (tmp.dbName().equals(box.value()) && tmp.dbType().equals(box.dbType())\n\t\t\t\t\t&& tmp.transactionType().equals(box.transaction())) {\n\t\t\t\treturn tmp;\n\t\t\t}\n\t\t}\n\t\tint index = boxs.length;\n\t\tDBSource[] box2 = new DBSource[index + 1];\n\t\tSystem.arraycopy(boxs, 0, box2, 0, boxs.length);\n\t\tDBSource db = new DefaultDBSource(box.value(), box.dbType(), box.transaction());\n\t\tbox2[index] = db;\n\t\tthis.boxs = box2;\n\t\treturn db;\n\t}\n\n\t@Override\n\tpublic synchronized DBSource willProxy(Class<?> clz, Method rawMethod) {\n\t\tBoxSpec spec = DBSpecs.extractBox(rawMethod);\n\t\tif (spec == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this.changeToDBSource(spec);\n\t}\n\n\t@Override\n\tpublic AopExecutor get(Object obj) {\n\t\tDBSource dbSource = (DBSource) obj;\n\t\treturn new DBTransaction(dbSource);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/exec/DBExecutor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.exec;\n\n@FunctionalInterface\npublic interface DBExecutor<T> {\n\n\tT execute(DBSource d) throws Exception;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/exec/DBSource.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.exec;\n\nimport org.yx.db.enums.DBType;\nimport org.yx.db.enums.TransactionType;\n\npublic interface DBSource {\n\n\tString dbName();\n\n\tDBType dbType();\n\n\tTransactionType transactionType();\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/exec/DBSources.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.exec;\n\nimport org.yx.conf.Const;\nimport org.yx.db.enums.DBType;\nimport org.yx.db.enums.TransactionType;\n\npublic final class DBSources {\n\n\tprivate static final DBSource WRITE = new DefaultDBSource(Const.DEFAULT_DB_NAME, DBType.WRITE,\n\t\t\tTransactionType.REQUIRED);\n\tprivate static final DBSource READONLY = new DefaultDBSource(Const.DEFAULT_DB_NAME, DBType.READONLY,\n\t\t\tTransactionType.REQUIRED);\n\tprivate static final DBSource ANY = new DefaultDBSource(Const.DEFAULT_DB_NAME, DBType.ANY,\n\t\t\tTransactionType.REQUIRED);\n\n\tpublic static DBSource write() {\n\t\treturn WRITE;\n\t}\n\n\tpublic static DBSource readOnly() {\n\t\treturn READONLY;\n\t}\n\n\tpublic static DBSource any() {\n\t\treturn ANY;\n\t}\n\n\tpublic static DBSource write(String dbName) {\n\t\treturn new DefaultDBSource(dbName, DBType.WRITE, TransactionType.REQUIRED);\n\t}\n\n\tpublic static DBSource readOnly(String dbName) {\n\t\treturn new DefaultDBSource(dbName, DBType.READONLY, TransactionType.REQUIRED);\n\t}\n\n\tpublic static DBSource any(String dbName) {\n\t\treturn new DefaultDBSource(dbName, DBType.ANY, TransactionType.REQUIRED);\n\t}\n\n\tpublic static DBSource writeAutoCommit(String dbName) {\n\t\treturn new DefaultDBSource(dbName, DBType.WRITE, TransactionType.AUTO_COMMIT);\n\t}\n\n\tpublic static DBSource autoCommit(String dbName) {\n\t\treturn new DefaultDBSource(dbName, DBType.ANY, TransactionType.AUTO_COMMIT);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/exec/DBTransaction.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.exec;\n\nimport java.sql.SQLException;\n\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.bean.aop.AopExecutor;\nimport org.yx.db.conn.ConnectionPool;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\n\npublic final class DBTransaction implements AopExecutor, AutoCloseable {\n\n\tprivate ConnectionPool dbCtx = null;\n\n\t@NotNull\n\tprivate final DBSource box;\n\n\tpublic DBTransaction(@NotNull DBSource box) {\n\t\tthis.box = box;\n\t}\n\n\tpublic void begin() {\n\t\tswitch (box.transactionType()) {\n\t\tcase AUTO_COMMIT:\n\t\t\tdbCtx = ConnectionPool.create(box.dbName(), box.dbType(), true);\n\t\t\treturn;\n\t\tcase REQUIRES_NEW:\n\t\t\tdbCtx = ConnectionPool.create(box.dbName(), box.dbType(), false);\n\t\t\treturn;\n\t\tcase REQUIRED:\n\n\t\t\tdbCtx = ConnectionPool.createIfAbsent(box.dbName(), box.dbType());\n\t\t\treturn;\n\t\tdefault:\n\t\t\treturn;\n\t\t}\n\t}\n\n\tpublic void rollback(Throwable e) {\n\t\tif (dbCtx == null || dbCtx.isAutoCommit()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tdbCtx.rollback(e);\n\t\t} catch (SQLException e1) {\n\t\t\tLog.printStack(\"sumk.sql.error\", e1);\n\t\t}\n\t}\n\n\tpublic void commit() {\n\t\tif (dbCtx == null || dbCtx.isAutoCommit()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tthis.dbCtx.commit();\n\t\t} catch (SQLException e) {\n\t\t\tLog.printStack(\"sumk.sql.error\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() {\n\n\t\tif (dbCtx == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tdbCtx.close();\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(7820198, \"error in commit,\" + e.getMessage(), e);\n\t\t}\n\t}\n\n\tpublic ConnectionPool getConnectionPool() {\n\t\treturn dbCtx;\n\t}\n\n\tpublic DBSource getDBSource() {\n\t\treturn box;\n\t}\n\n\t@Override\n\tpublic void before(Object[] params) {\n\t\tthis.begin();\n\t}\n\n\t@Override\n\tpublic Throwable after(Object result, Throwable e, boolean methodExecuted) {\n\t\ttry {\n\t\t\tif (e == null) {\n\t\t\t\tthis.commit();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tthis.rollback(e);\n\t\t\treturn e;\n\t\t} finally {\n\t\t\tthis.close();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/exec/DefaultDBSource.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.exec;\n\nimport java.util.Objects;\n\nimport org.yx.db.enums.DBType;\nimport org.yx.db.enums.TransactionType;\n\npublic class DefaultDBSource implements DBSource {\n\tprivate final String dbName;\n\tprivate final DBType type;\n\tprivate final TransactionType transactionType;\n\n\tpublic DefaultDBSource(String dbName, DBType type, TransactionType transactionType) {\n\t\tthis.dbName = Objects.requireNonNull(dbName);\n\t\tthis.type = Objects.requireNonNull(type);\n\t\tthis.transactionType = Objects.requireNonNull(transactionType);\n\t}\n\n\tpublic static DefaultDBSource create(String dbName, DBType type, TransactionType transactionType) {\n\t\treturn new DefaultDBSource(dbName, type, transactionType);\n\t}\n\n\tpublic String dbName() {\n\t\treturn dbName;\n\t}\n\n\tpublic DBType dbType() {\n\t\treturn type;\n\t}\n\n\t@Override\n\tpublic TransactionType transactionType() {\n\t\treturn this.transactionType;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DefaultDatabase [dbName=\" + dbName + \", type=\" + type + \", transactionType=\" + transactionType + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/kit/DBKits.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.kit;\n\nimport java.sql.PreparedStatement;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.db.sql.PojoMetaHolder;\nimport org.yx.db.visit.RecordRepository;\nimport org.yx.exception.SumkException;\nimport org.yx.exception.SumkExceptionCode;\nimport org.yx.util.StringUtil;\n\npublic final class DBKits {\n\n\tprivate static Function<PreparedStatement, String> plainSqlParse = statement -> {\n\t\tString sql = String.valueOf(statement);\n\t\tint index = sql.indexOf(\": \");\n\t\tif (index < 10 || index + 10 > sql.length()) {\n\t\t\treturn sql;\n\t\t}\n\t\treturn sql.substring(index + 2);\n\t};\n\n\tpublic static void setPlainSqlParse(Function<PreparedStatement, String> plainSqlParse) {\n\t\tDBKits.plainSqlParse = Objects.requireNonNull(plainSqlParse);\n\t}\n\n\tpublic static String getSqlOfStatement(PreparedStatement statement) {\n\t\treturn plainSqlParse.apply(statement);\n\t}\n\n\tpublic static <T> T queryOne(List<T> list) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (list.size() > 1) {\n\t\t\tthrow new SumkException(SumkExceptionCode.DB_TOO_MANY_RESULTS,\n\t\t\t\t\t\"result size:\" + list.size() + \" more than one\");\n\t\t}\n\t\treturn list.get(0);\n\t}\n\n\t@SafeVarargs\n\tpublic static <T> int clearCache(T... pojos) throws Exception {\n\t\tint total = 0;\n\t\tif (pojos == null || pojos.length == 0) {\n\t\t\treturn total;\n\t\t}\n\t\tT t = null;\n\t\tfor (int i = 0; i < pojos.length; i++) {\n\t\t\tt = pojos[i];\n\t\t\tif (t != null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (t == null) {\n\t\t\treturn total;\n\t\t}\n\t\tPojoMeta pm = PojoMetaHolder.getPojoMeta(t.getClass(), null);\n\t\tif (pm == null || pm.isNoCache()) {\n\t\t\treturn total;\n\t\t}\n\t\tfor (T src : pojos) {\n\t\t\tif (src == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString id = pm.getCacheID(src, false);\n\t\t\tif (StringUtil.isEmpty(id)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tRecordRepository.del(pm, id);\n\t\t\ttotal++;\n\t\t}\n\t\treturn total;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/kit/SDBuilder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.kit;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.common.util.S;\nimport org.yx.db.SDB;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.util.Loader;\n\npublic final class SDBuilder {\n\tprivate String name;\n\tprivate Map<String, Object> param;\n\n\tpublic SDBuilder name(String name) {\n\t\tthis.name = name;\n\t\treturn this;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic SDBuilder param(Object p) {\n\t\tif (p == null) {\n\t\t\tthis.param = null;\n\t\t\treturn this;\n\t\t}\n\t\tif (p instanceof Map) {\n\t\t\tthis.param = (Map<String, Object>) p;\n\t\t\treturn this;\n\t\t}\n\t\tthis.param = S.bean().beanToMap(p, false);\n\t\treturn this;\n\t}\n\n\tpublic <T> List<T> list(Class<T> clz) {\n\t\tList<Map<String, Object>> list = SDB.list(name, param);\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<T> retList = new ArrayList<>();\n\t\tfor (Map<String, Object> ret : list) {\n\t\t\tretList.add(toBean(ret, clz));\n\t\t}\n\t\treturn retList;\n\t}\n\n\tpublic <T> T queryOne(Class<T> clz) {\n\t\tMap<String, Object> ret = SDB.queryOne(name, param);\n\t\treturn toBean(ret, clz);\n\t}\n\n\tprivate <T> T toBean(Map<String, Object> ret, Class<T> clz) {\n\t\tif (ret == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn S.bean().fillBeanIgnoreCaseAndUnderLine(ret, newInstance(clz));\n\t}\n\n\tprivate <T> T newInstance(Class<T> clz) {\n\t\ttry {\n\t\t\treturn Loader.newInstance(clz);\n\t\t} catch (Exception e) {\n\t\t\tLogs.db().error(e.toString(), e);\n\t\t\tthrow new SumkException(234125435, \"创建\" + clz.getName() + \"的实例失败，可能是它没有无参的构造函数\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/listener/DeleteListener.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.listener;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.annotation.Bean;\nimport org.yx.common.listener.SumkListener;\nimport org.yx.conf.Const;\nimport org.yx.db.event.DeleteEvent;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.db.visit.RecordRepository;\n\n@Bean\npublic class DeleteListener implements SumkListener {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 92;\n\t}\n\n\t@Override\n\tpublic Collection<String> acceptType() {\n\t\treturn Collections.singletonList(Const.LISTENER_DB_MODIFY);\n\t}\n\n\t@Override\n\tpublic void listen(Object ev) throws Exception {\n\t\tif (!DBSettings.toCache() || !(ev instanceof DeleteEvent)) {\n\t\t\treturn;\n\t\t}\n\t\tDeleteEvent event = (DeleteEvent) ev;\n\t\tPojoMeta pm = event.getTableMeta();\n\t\tif (pm == null || pm.isNoCache()) {\n\t\t\treturn;\n\t\t}\n\t\tList<Map<String, Object>> wheres = event.getWheres();\n\t\tif (wheres == null || wheres.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (Map<String, Object> src : wheres) {\n\t\t\tString id = pm.getCacheID(src, true);\n\t\t\tRecordRepository.del(pm, id);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/listener/InsertListener.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.listener;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.annotation.Bean;\nimport org.yx.common.listener.SumkListener;\nimport org.yx.conf.Const;\nimport org.yx.db.DBJson;\nimport org.yx.db.enums.CacheType;\nimport org.yx.db.event.InsertEvent;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.db.visit.RecordRepository;\n\n@Bean\npublic class InsertListener implements SumkListener {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 90;\n\t}\n\n\t@Override\n\tpublic Collection<String> acceptType() {\n\t\treturn Collections.singletonList(Const.LISTENER_DB_MODIFY);\n\t}\n\n\t@Override\n\tpublic void listen(Object ev) throws Exception {\n\t\tif (!DBSettings.toCache() || !(ev instanceof InsertEvent)) {\n\t\t\treturn;\n\t\t}\n\t\tInsertEvent event = (InsertEvent) ev;\n\t\tPojoMeta pm = event.getTableMeta();\n\t\tList<Map<String, Object>> list = event.getPojos();\n\t\tif (pm == null || pm.isNoCache() || list == null) {\n\t\t\treturn;\n\t\t}\n\t\tfor (Map<String, Object> map : list) {\n\t\t\tString id = pm.getCacheID(map, false);\n\t\t\tif (id == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (pm.cacheType() == CacheType.LIST) {\n\t\t\t\tRecordRepository.del(pm, id);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tRecordRepository.set(pm, id, DBJson.operator().toJson(map));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/listener/SelectListener.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.listener;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ThreadLocalRandom;\n\nimport org.yx.annotation.Bean;\nimport org.yx.common.listener.SumkListener;\nimport org.yx.conf.Const;\nimport org.yx.db.DBJson;\nimport org.yx.db.conn.ConnectionPool;\nimport org.yx.db.enums.CacheType;\nimport org.yx.db.event.QueryEvent;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.db.visit.RecordRepository;\n\n@Bean\npublic class SelectListener implements SumkListener {\n\n\t@Override\n\tpublic Collection<String> acceptType() {\n\t\treturn Collections.singletonList(Const.LISTENER_DB_QUERY);\n\t}\n\n\t@Override\n\tpublic void listen(Object ev) throws Exception {\n\t\tif (!(ev instanceof QueryEvent)) {\n\t\t\treturn;\n\t\t}\n\t\tQueryEvent event = (QueryEvent) ev;\n\t\tPojoMeta pm = event.getTableMeta();\n\n\t\tif (pm == null || pm.isNoCache() || event.getResult() == null || ConnectionPool.get().existModifyEvent(pm)) {\n\t\t\treturn;\n\t\t}\n\t\tList<Map<String, Object>> in = event.getIn();\n\t\tif (in == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (in.size() != 1) {\n\t\t\tif (pm.isPrimeKeySameWithCache() && pm.getDatabaseIds().size() == 1 && pm.cacheType() == CacheType.SINGLE) {\n\t\t\t\tthis.singleIdCache(pm, event.getResult());\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tMap<String, Object> where = in.get(0);\n\t\tif (!pm.isOnlyCacheID(where)) {\n\t\t\treturn;\n\t\t}\n\t\tString id = pm.getCacheID(where, false);\n\t\tif (id == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<Object> list = new ArrayList<>(4);\n\t\tfor (Object obj : event.getResult()) {\n\t\t\tif (id.equals(pm.getCacheID(obj, false))) {\n\t\t\t\tlist.add(obj);\n\t\t\t}\n\t\t}\n\t\tif (list.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (pm.cacheType() == CacheType.LIST) {\n\t\t\tRecordRepository.set(pm, id, DBJson.operator().toJson(list));\n\t\t\treturn;\n\t\t}\n\t\tif (list.size() != 1 || list.get(0) == null) {\n\t\t\treturn;\n\t\t}\n\t\tRecordRepository.set(pm, id, DBJson.operator().toJson(list.get(0)));\n\t}\n\n\tprivate void singleIdCache(PojoMeta pm, List<?> result) throws Exception {\n\t\tint size = result.size();\n\t\tint max = DBSettings.maxSingleKeyToCache();\n\t\tif (size > max) {\n\t\t\tsize = max;\n\t\t\tresult = new ArrayList<>(result);\n\t\t\tCollections.shuffle(result, ThreadLocalRandom.current());\n\t\t}\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tObject obj = result.get(i);\n\t\t\tif (obj == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString id = pm.getCacheID(obj, false);\n\t\t\tif (id == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tRecordRepository.set(pm, id, DBJson.operator().toJson(obj));\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/listener/UpdateListener.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.listener;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.annotation.Bean;\nimport org.yx.common.listener.SumkListener;\nimport org.yx.conf.Const;\nimport org.yx.db.DBJson;\nimport org.yx.db.enums.CacheType;\nimport org.yx.db.event.UpdateEvent;\nimport org.yx.db.sql.ColumnMeta;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.db.visit.RecordRepository;\n\n@Bean\npublic class UpdateListener implements SumkListener {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 91;\n\t}\n\n\t@Override\n\tpublic Collection<String> acceptType() {\n\t\treturn Collections.singletonList(Const.LISTENER_DB_MODIFY);\n\t}\n\n\t@Override\n\tpublic void listen(Object ev) throws Exception {\n\t\tif (!DBSettings.toCache() || !(ev instanceof UpdateEvent)) {\n\t\t\treturn;\n\t\t}\n\t\tUpdateEvent event = (UpdateEvent) ev;\n\t\tPojoMeta pm = event.getTableMeta();\n\t\tif (pm == null || pm.isNoCache()) {\n\t\t\treturn;\n\t\t}\n\t\tList<Map<String, Object>> wheres = event.getWheres();\n\t\tboolean canUpdateCache = event.canUpdateCache();\n\t\tfor (Map<String, Object> where : wheres) {\n\n\t\t\thandleUpdate(event, pm, where, canUpdateCache);\n\t\t}\n\n\t}\n\n\tprivate void handleUpdate(UpdateEvent event, PojoMeta pm, Map<String, Object> where, boolean canUpdateCache)\n\t\t\tthrows Exception {\n\t\tString id = pm.getCacheID(where, true);\n\t\tMap<String, Object> to = new HashMap<>(event.getTo());\n\t\tif (!event.isUpdateDBID()) {\n\t\t\tList<ColumnMeta> m_ids = pm.getDatabaseIds();\n\t\t\tif (m_ids != null && m_ids.size() > 0) {\n\t\t\t\tfor (ColumnMeta m : m_ids) {\n\t\t\t\t\tString name = m.getFieldName();\n\t\t\t\t\tObject v = where.get(name);\n\t\t\t\t\tif (v != null) {\n\t\t\t\t\t\tto.put(name, v);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tto.remove(name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (canUpdateCache) {\n\t\t\tString id_new = pm.getCacheID(to, true);\n\t\t\tif (!id.equals(id_new)) {\n\t\t\t\tRecordRepository.del(pm, id);\n\t\t\t}\n\t\t\tif (pm.cacheType() == CacheType.LIST || event.getIncrMap() != null) {\n\t\t\t\tRecordRepository.del(pm, id_new);\n\t\t\t} else {\n\t\t\t\tRecordRepository.set(pm, id_new, DBJson.operator().toJson(to));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tRecordRepository.del(pm, id);\n\n\t\tMap<String, Object> where2 = new HashMap<>(where);\n\t\twhere2.putAll(to);\n\t\tString id_new = pm.getCacheID(where2, true);\n\n\t\tif (id.equals(id_new)) {\n\t\t\treturn;\n\t\t}\n\t\tRecordRepository.del(pm, id_new);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/log/SimpleSqlLogImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.log;\n\nimport java.util.Objects;\n\nimport org.yx.base.context.ActionContext;\nimport org.yx.base.sumk.UnsafeStringWriter;\nimport org.yx.db.DBJson;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.db.sql.MapedSql;\nimport org.yx.db.sql.SqlLog;\nimport org.yx.db.visit.SumkStatement;\nimport org.yx.log.Log;\nimport org.yx.log.LogKits;\nimport org.yx.log.LogLevel;\nimport org.yx.log.Logs;\nimport org.yx.log.impl.CodeLine;\nimport org.yx.log.impl.CodeLineKit;\nimport org.yx.log.impl.LogObject;\nimport org.yx.log.impl.UnionLog;\nimport org.yx.log.impl.UnionLogs;\nimport org.yx.util.SumkDate;\n\nimport com.google.gson.stream.JsonWriter;\n\npublic class SimpleSqlLogImpl implements SqlLog {\n\tprivate static String LOG_NAME_SQL = \"sumk.unionlog.sql\";\n\n\tpublic SimpleSqlLogImpl() {\n\t\tUnionLog union = UnionLogs.getUnionLog();\n\t\tLogs.db().debug(\"sqlLog初始化时，UnionLog的状态为:{}\", union.isStarted());\n\t}\n\n\tpublic static String getSqlLogName() {\n\t\treturn LOG_NAME_SQL;\n\t}\n\n\tpublic static void setSqlLogName(String sqlLogName) {\n\t\tsqlLogName = Objects.requireNonNull(sqlLogName).trim();\n\t\tif (sqlLogName.length() > 0) {\n\t\t\tLOG_NAME_SQL = sqlLogName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void log(SumkStatement state, int totalTime, Throwable ex) {\n\t\tUnionLog union = UnionLogs.getUnionLog();\n\t\tif (!union.isStarted()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tMapedSql msql = state.getMaped();\n\t\t\tUnsafeStringWriter stringWriter = new UnsafeStringWriter(64);\n\t\t\tJsonWriter writer = new JsonWriter(stringWriter);\n\t\t\twriter.setSerializeNulls(true);\n\t\t\twriter.beginObject();\n\t\t\twriter.name(\"sql\").value(msql.getSql());\n\t\t\twriter.name(\"hash\").value(msql.getSql().hashCode() & Integer.MAX_VALUE);\n\t\t\tString params = DBJson.operator().toJson(msql.getParamters());\n\t\t\tparams = LogKits.shorterSubfix(params, DBSettings.maxSqlParamLength());\n\t\t\twriter.name(\"paramters\").value(params);\n\t\t\twriter.name(\"sqlTime\").value(state.getSqlTime());\n\t\t\tif (state.getModifyCount() > -1) {\n\t\t\t\twriter.name(\"modifyCount\").value(state.getModifyCount());\n\t\t\t}\n\t\t\tif (totalTime > -1 && totalTime != state.getSqlTime()) {\n\t\t\t\twriter.name(\"totalTime\").value(totalTime);\n\t\t\t}\n\t\t\twriter.endObject();\n\t\t\twriter.flush();\n\t\t\twriter.close();\n\t\t\tCodeLine codeLine = CodeLineKit.parse(SumkStatement.getMarker(), LOG_NAME_SQL);\n\t\t\tLogLevel methodLevel = ex == null ? LogLevel.INFO : LogLevel.ERROR;\n\t\t\tLogObject logObj = new LogObject(LOG_NAME_SQL, SumkDate.now(), methodLevel, stringWriter.toString(), ex,\n\t\t\t\t\tThread.currentThread().getName(), ActionContext.current().logContext(), codeLine);\n\t\t\tunion.directOffer(logObj);\n\t\t} catch (Exception e) {\n\t\t\tLog.get(\"sumk.log\").error(e.getMessage(), e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/DBPlugin.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.io.ByteArrayInputStream;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.yx.annotation.Bean;\nimport org.yx.bean.Plugin;\nimport org.yx.common.monitor.Monitors;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.MultiResourceLoader;\nimport org.yx.db.conn.DataSources;\nimport org.yx.db.event.DBEventPublisher;\nimport org.yx.db.log.SimpleSqlLogImpl;\nimport org.yx.db.monitor.DBMonitor;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.db.visit.SumkStatement;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.util.SumkDate;\n\n@Bean\npublic class DBPlugin implements Plugin {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 90;\n\t}\n\n\t@Override\n\tpublic void prepare() {\n\t\tDBSettings.init();\n\t\tDBEventPublisher.init();\n\t\tloadSDBResources();\n\t\ttry {\n\t\t\tSumkStatement.setSqlLog(new SimpleSqlLogImpl());\n\t\t} catch (Throwable e) {\n\t\t\tLogs.db().warn(\"因为没有使用async-logger，所以不加载数据库的统一日志适配器\");\n\t\t}\n\n\t\tMonitors.add(new DBMonitor());\n\t\tLogs.db().info(\"数据库插件启动完成\");\n\t}\n\n\t@Override\n\tpublic void startAsync() {\n\n\t\tpreHotDataSource();\n\t}\n\n\tprotected void preHotDataSource() {\n\t\tif (AppInfo.getBoolean(\"sumk.db.pool.prehot.disable\", false)) {\n\t\t\treturn;\n\t\t}\n\t\tMap<String, String> map = AppInfo.subMap(\"s.db.\");\n\t\tif (map == null || map.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (String key : map.keySet()) {\n\t\t\tint index = key.indexOf('.');\n\t\t\tif (index > 0) {\n\t\t\t\tkey = key.substring(0, index);\n\t\t\t}\n\t\t\tLogs.db().debug(\"{} begin preHot...\", key);\n\t\t\tDataSources.getManager(key);\n\t\t}\n\t}\n\n\tprotected void loadSDBResources() {\n\t\ttry {\n\t\t\tMultiResourceLoader loader = SqlHolder.resourceLoader().get();\n\t\t\tloadSql(loader);\n\t\t\tstartListen(loader);\n\t\t} catch (Throwable e) {\n\t\t\tthrow new SumkException(2351343, \"sdb加载本地sql文件失败\", e);\n\t\t}\n\t}\n\n\tprivate void startListen(MultiResourceLoader loader) {\n\t\tloader.startListen(load -> {\n\t\t\ttry {\n\t\t\t\tLogs.db().info(\"local sql changed at {}\", SumkDate.now().to_yyyy_MM_dd_HH_mm_ss());\n\t\t\t\tloadSql(load);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.printStack(\"sumk.sql.error\", e);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void loadSql(MultiResourceLoader loader) throws Exception {\n\t\tMap<String, byte[]> inputMap = loader.openResources(null);\n\t\tif (inputMap == null || inputMap.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tMap<String, SqlParser> sqlMap = new HashMap<>();\n\t\ttry {\n\t\t\tfor (Entry<String, byte[]> entry : inputMap.entrySet()) {\n\t\t\t\tString fileName = entry.getKey();\n\t\t\t\tbyte[] bs = entry.getValue();\n\t\t\t\tif (bs == null || bs.length == 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tSqlXmlParser.parseXml(sqlMap, SqlHolder.documentBuilderFactory().get(), fileName,\n\t\t\t\t\t\tnew ByteArrayInputStream(bs));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLogs.db().error(e.getLocalizedMessage(), e);\n\t\t\treturn;\n\t\t}\n\t\tSqlHolder.setSQLS(sqlMap);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/ForeachParser.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport org.yx.db.sql.MapedSql;\nimport org.yx.db.sql.MapedSqlBuilder;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\n\npublic class ForeachParser implements SqlParser {\n\n\tprivate final JoinerFactory joinFactory;\n\tprivate final String itemName;\n\tprivate final String collecitonName;\n\t/**\n\t * #{@} 是列表中值的占位符\n\t */\n\tprivate final String template;\n\n\tpublic static ForeachParser create(String collection, String itemName, String template, JoinerFactory joinFactory) {\n\t\tif (template == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (itemName != null) {\n\t\t\titemName = itemName.trim();\n\t\t\tif (itemName.isEmpty()) {\n\t\t\t\titemName = null;\n\t\t\t}\n\t\t}\n\t\treturn new ForeachParser(collection, itemName, template, joinFactory);\n\t}\n\n\tprotected ForeachParser(String collecitonName, String itemName, String template, JoinerFactory joinFactory) {\n\t\tthis.collecitonName = Objects.requireNonNull(collecitonName);\n\t\tthis.itemName = itemName;\n\t\tthis.template = Objects.requireNonNull(template).trim();\n\t\tthis.joinFactory = Objects.requireNonNull(joinFactory);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic MapedSql toMapedSql(Map<String, Object> param) throws Exception {\n\t\tObject obj = param.get(collecitonName);\n\t\tif (obj == null) {\n\t\t\tif (Logs.db().isTraceEnabled()) {\n\t\t\t\tLogs.db().trace(\"{} is null\", collecitonName);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tif (obj instanceof Collection) {\n\t\t\tCollection<?> list = (Collection<?>) obj;\n\t\t\tList<MapedSql> mapeds = new ArrayList<>(list.size());\n\n\t\t\tMapedSqlBuilder builder;\n\t\t\tfor (Object v : list) {\n\t\t\t\tif (this.itemName == null) {\n\t\t\t\t\tbuilder = new MapedSqlBuilder(template, param);\n\t\t\t\t} else if (v instanceof Map) {\n\t\t\t\t\tbuilder = new MapedSqlBuilder(template,\n\t\t\t\t\t\t\tnew ComposeMapHandler(param, (Map<String, Object>) v, this.itemName + \".\"));\n\t\t\t\t} else {\n\t\t\t\t\tbuilder = new MapedSqlBuilder(template, new ComposeValueHandler(param, v, this.itemName));\n\t\t\t\t}\n\t\t\t\tMapedSql maped = builder.toMapedSql();\n\t\t\t\tif (maped != null) {\n\t\t\t\t\tmapeds.add(maped);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (mapeds.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn MapedSql.merge(mapeds, joinFactory.create());\n\t\t}\n\t\tthrow new SumkException(235345346, \"field \" + collecitonName + \" is not a Collection instance,it's type is \"\n\t\t\t\t+ obj.getClass().getSimpleName());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Foreach [colleciton=\" + collecitonName + \", item=\" + itemName + \", joinFactory=\" + joinFactory + \" : \"\n\t\t\t\t+ template + \"]\";\n\t}\n\n\tprivate static class ComposeMapHandler implements Function<String, Object> {\n\n\t\tprivate final Map<String, Object> globalMap;\n\t\tprivate final Map<String, Object> itemMap;\n\t\tprivate final String prefix;\n\n\t\tpublic ComposeMapHandler(Map<String, Object> globalMap, Map<String, Object> itemMap, String prefix) {\n\t\t\tthis.globalMap = globalMap;\n\t\t\tthis.itemMap = itemMap;\n\t\t\tthis.prefix = prefix;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object apply(String key) {\n\t\t\tif (key.startsWith(this.prefix)) {\n\t\t\t\treturn this.itemMap.get(key.substring(this.prefix.length()));\n\t\t\t}\n\t\t\treturn this.globalMap.get(key);\n\t\t}\n\n\t}\n\n\tprivate static class ComposeValueHandler implements Function<String, Object> {\n\n\t\tprivate final Map<String, Object> globalMap;\n\t\tprivate final Object item;\n\t\tprivate final String itemName;\n\n\t\tpublic ComposeValueHandler(Map<String, Object> globalMap, Object item, String itemName) {\n\t\t\tthis.globalMap = globalMap;\n\t\t\tthis.item = item;\n\t\t\tthis.itemName = itemName;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object apply(String key) {\n\t\t\tif (key.equals(itemName)) {\n\t\t\t\treturn item;\n\t\t\t}\n\t\t\treturn this.globalMap.get(key);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/IFParser.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Predicate;\n\nimport org.yx.db.sql.MapedSql;\n\npublic class IFParser implements SqlParser {\n\n\tprivate final SqlParser parser;\n\tprivate final Predicate<Map<String, Object>> expression;\n\n\tpublic static IFParser create(Predicate<Map<String, Object>> expression, SqlParser parser) {\n\t\tif (parser == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new IFParser(expression, parser);\n\t}\n\n\tprotected IFParser(Predicate<Map<String, Object>> expression, SqlParser parser) {\n\t\tthis.expression = Objects.requireNonNull(expression);\n\t\tthis.parser = Objects.requireNonNull(parser);\n\t}\n\n\t@Override\n\tpublic MapedSql toMapedSql(Map<String, Object> map) throws Exception {\n\t\treturn expression.test(map) ? parser.toMapedSql(map) : null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"IF(\" + expression + \"): \" + this.parser;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/ItemsParser.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.yx.db.sql.MapedSql;\n\npublic class ItemsParser implements SqlParser {\n\n\tprivate final JoinerFactory joinFactory;\n\tprivate final SqlParser[] parsers;\n\n\tpublic static ItemsParser create(List<SqlParser> list, JoinerFactory joinFactory) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new ItemsParser(list.toArray(new SqlParser[list.size()]), joinFactory);\n\t}\n\n\tprotected ItemsParser(SqlParser[] parsers, JoinerFactory joinFactory) {\n\t\tthis.parsers = Objects.requireNonNull(parsers);\n\t\tthis.joinFactory = Objects.requireNonNull(joinFactory);\n\t}\n\n\t@Override\n\tpublic MapedSql toMapedSql(Map<String, Object> param) throws Exception {\n\t\tList<MapedSql> list = new ArrayList<>(this.parsers.length);\n\t\tfor (SqlParser p : this.parsers) {\n\t\t\tMapedSql ms = p.toMapedSql(param);\n\t\t\tif (ms == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.add(ms);\n\t\t}\n\t\tif (list.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn MapedSql.merge(list, this.joinFactory.create());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ItemsParser [joinFactory=\" + joinFactory + \", parsers=\" + Arrays.toString(parsers) + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/JoinerFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport org.yx.base.ItemJoiner;\n\npublic class JoinerFactory {\n\tprivate final CharSequence delimiter;\n\tprivate final CharSequence prefix;\n\tprivate final CharSequence suffix;\n\n\tpublic static JoinerFactory create(String delimiter, String prefix, String suffix) {\n\t\tdelimiter = addBlank(delimiter);\n\t\treturn new JoinerFactory(delimiter == null ? \" \" : delimiter, addBlank(prefix), addBlank(suffix));\n\t}\n\n\tprivate static String addBlank(String p) {\n\t\tif (p == null) {\n\t\t\treturn p;\n\t\t}\n\t\tp = p.trim();\n\t\tif (p.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn \" \" + p + \" \";\n\t}\n\n\tprivate JoinerFactory(String delimiter, String prefix, String suffix) {\n\t\tthis.delimiter = delimiter;\n\t\tthis.prefix = prefix;\n\t\tthis.suffix = suffix;\n\t}\n\n\tpublic ItemJoiner create() {\n\t\treturn new ItemJoiner(delimiter, prefix, suffix);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"{\" + prefix + \" - \" + delimiter + \" - \" + suffix + \"}\";\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/NamedExecutor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.db.sql.InsertResult;\nimport org.yx.db.sql.MapedSql;\nimport org.yx.db.sql.SqlBuilder;\nimport org.yx.db.visit.Visitors;\nimport org.yx.exception.SumkException;\n\n/**\n * 这里的sql，占位符不是?，而是#{**}，里面的name，与map的key要一一对应<BR>\n * \n * @author 游夏\n *\n */\npublic final class NamedExecutor {\n\n\tprivate static int toSqlCount;\n\n\tpublic static int getExecuteCount() {\n\t\treturn toSqlCount;\n\t}\n\n\tprivate static class InnerSqlBuilder implements SqlBuilder {\n\n\t\tprivate final Map<String, Object> map;\n\t\tprivate final SqlParser sqlParser;\n\n\t\tInnerSqlBuilder(SqlParser sql, Map<String, Object> map) {\n\t\t\tthis.sqlParser = sql;\n\t\t\tthis.map = map == null ? Collections.emptyMap() : new HashMap<>(map);\n\t\t}\n\n\t\t@Override\n\t\tpublic MapedSql toMapedSql() throws Exception {\n\t\t\ttoSqlCount++;\n\t\t\treturn sqlParser.toMapedSql(map);\n\t\t}\n\n\t}\n\n\tprivate static InnerSqlBuilder createSqlBuilder(SqlParser sql, Map<String, Object> map) {\n\t\treturn new InnerSqlBuilder(sql, map);\n\t}\n\n\tpublic static int execute(SqlParser sql, Map<String, Object> map) {\n\t\ttry {\n\t\t\treturn Visitors.modifyVisitor.visit(createSqlBuilder(sql, map));\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\tpublic static InsertResult insertWithAutoGeneratedKeys(SqlParser sql, Map<String, Object> map) {\n\t\ttry {\n\t\t\treturn Visitors.insertWithAutoGeneratedKeysVisitor.visit(createSqlBuilder(sql, map));\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\tpublic static List<Map<String, Object>> list(SqlParser sql, Map<String, Object> map) {\n\t\ttry {\n\t\t\treturn Visitors.queryVisitor.visit(createSqlBuilder(sql, map));\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\tpublic static List<Object[]> listInArray(SqlParser sql, Map<String, Object> map) {\n\t\ttry {\n\t\t\treturn Visitors.arrayListQueryVisitor.visit(createSqlBuilder(sql, map));\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\tpublic static List<?> singleColumnList(SqlParser sql, Map<String, Object> map) {\n\t\ttry {\n\t\t\treturn Visitors.singleListQueryVisitor.visit(createSqlBuilder(sql, map));\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\tpublic static long count(SqlParser sql, Map<String, Object> map) {\n\t\ttry {\n\t\t\tList<?> list = Visitors.singleListQueryVisitor.visit(createSqlBuilder(sql, map));\n\t\t\tNumber n = (Number) list.get(0);\n\t\t\treturn n.longValue();\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/PureStringParser.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.util.Map;\n\nimport org.yx.db.sql.MapedSql;\nimport org.yx.db.sql.MapedSqlBuilder;\n\npublic class PureStringParser implements SqlParser {\n\n\tprivate final String sql;\n\n\tpublic static PureStringParser create(String sql) {\n\t\tif (sql == null || sql.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new PureStringParser(sql);\n\t}\n\n\tprivate PureStringParser(String sql) {\n\t\tthis.sql = sql.trim();\n\t}\n\n\t@Override\n\tpublic MapedSql toMapedSql(Map<String, Object> map) throws Exception {\n\t\treturn new MapedSqlBuilder(sql, map).toMapedSql();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn sql;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/RawExecutor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.db.sql.InsertResult;\nimport org.yx.db.sql.RawSqlBuilder;\nimport org.yx.db.visit.Visitors;\nimport org.yx.exception.SumkException;\n\n/**\n * 以?为占位符的原生sql。推荐通过常量或其它方式引用<BR>\n * sql的参数支持以List的方式传入\n * \n * @author 游夏\n *\n */\npublic class RawExecutor {\n\t/**\n\t * @param sql    以?为占位符的原生sql。\n\t * @param params 参数\n\t * @return sql执行结果\n\t */\n\tpublic static int execute(String sql, Object... params) {\n\t\ttry {\n\t\t\treturn Visitors.modifyVisitor.visit(new RawSqlBuilder(sql, params));\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\t/**\n\t * 这个方法用于需要获取自增长主键的值的情况\n\t * \n\t * @param sql    以?为占位符的原生sql。\n\t * @param params 参数\n\t * @return sql执行结果\n\t */\n\tpublic static InsertResult insertWithAutoGeneratedKeys(String sql, Object... params) {\n\t\ttry {\n\t\t\treturn Visitors.insertWithAutoGeneratedKeysVisitor.visit(new RawSqlBuilder(sql, params));\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\t/**\n\t * @param sql    以?为占位符的原生sql\n\t * @param params 参数\n\t * @return 结果集\n\t */\n\tpublic static List<Map<String, Object>> list(String sql, Object... params) {\n\t\ttry {\n\t\t\treturn Visitors.queryVisitor.visit(new RawSqlBuilder(sql, params));\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\t/**\n\t * @param sql    以?为占位符的原生sql\n\t * @param params 参数\n\t * @return 结果集\n\t */\n\tpublic static List<Object[]> listInOrder(String sql, Object... params) {\n\t\ttry {\n\t\t\treturn Visitors.arrayListQueryVisitor.visit(new RawSqlBuilder(sql, params));\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\t/**\n\t * 只有一个列的list方法<BR>\n\t * sum等函数是特殊的singleColumnList，它返回的list的size为1\n\t * \n\t * @param sql    以?为占位符的原生sql\n\t * @param params 参数\n\t * @return 结果集\n\t */\n\tpublic static List<?> singleColumnList(String sql, Object... params) {\n\t\ttry {\n\t\t\treturn Visitors.singleListQueryVisitor.visit(new RawSqlBuilder(sql, params));\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\t/**\n\t * @param sql    以?为占位符的原生sql\n\t * @param params 参数\n\t * @return 记录数\n\t */\n\tpublic static long count(String sql, Object... params) {\n\t\ttry {\n\t\t\tList<?> list = Visitors.singleListQueryVisitor.visit(new RawSqlBuilder(sql, params));\n\t\t\tNumber n = (Number) list.get(0);\n\t\t\treturn n.longValue();\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/SqlHolder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Supplier;\n\nimport javax.xml.parsers.DocumentBuilder;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.LocalMultiResourceLoaderSupplier;\nimport org.yx.conf.MultiResourceLoader;\nimport org.yx.exception.SumkException;\n\npublic class SqlHolder {\n\tprivate static Map<String, SqlParser> SQLS = new HashMap<>();\n\n\tpublic static void setSQLS(Map<String, SqlParser> sqls) {\n\t\tSQLS = sqls;\n\t}\n\n\tprivate static Supplier<DocumentBuilder> documentBuilderFactory = new SqlXmlBuilderFactory();\n\n\tprivate static Supplier<MultiResourceLoader> resourceLoader = new LocalMultiResourceLoaderSupplier(\n\t\t\tAppInfo.get(\"sumk.db.sql.path\", AppInfo.CLASSPATH_URL_PREFIX + \"sql\"));\n\n\tpublic static Supplier<MultiResourceLoader> resourceLoader() {\n\t\treturn resourceLoader;\n\t}\n\n\tpublic static void resourceLoader(Supplier<MultiResourceLoader> resourceLoader) {\n\t\tSqlHolder.resourceLoader = Objects.requireNonNull(resourceLoader);\n\t}\n\n\tpublic static Supplier<DocumentBuilder> documentBuilderFactory() {\n\t\treturn documentBuilderFactory;\n\t}\n\n\tpublic static void documentBuilderFactory(Supplier<DocumentBuilder> documentBuilderFactory) {\n\t\tSqlHolder.documentBuilderFactory = Objects.requireNonNull(documentBuilderFactory);\n\t}\n\n\tpublic static SqlParser findSql(String name) {\n\t\tSqlParser sql = SQLS.get(name);\n\t\tif (sql == null) {\n\t\t\tthrow new SumkException(64342451, \"sql [\" + name + \"] can not found \");\n\t\t}\n\t\treturn sql;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/SqlParser.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.util.Map;\n\nimport org.yx.db.sql.MapedSql;\n\npublic interface SqlParser {\n\n\tMapedSql toMapedSql(Map<String, Object> param) throws Exception;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/SqlParsers.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.yx.common.expression.Expressions;\nimport org.yx.common.expression.SimpleExpression;\nimport org.yx.exception.SumkException;\nimport org.yx.util.StringUtil;\n\npublic final class SqlParsers {\n\tprivate static final String SPLIT_AND = \",\";\n\tprivate static final String SPLIT_OR = \"\\\\|\";\n\n\tpublic static Predicate<Map<String, Object>> createParamExpression(String test, String matchType) {\n\t\tif (test == null || test.isEmpty()) {\n\t\t\tthrow new SumkException(34645465, \"if中的test不能为空\");\n\t\t}\n\t\ttest = StringUtil.toLatin(test.trim());\n\t\tboolean and = test.contains(SPLIT_AND);\n\t\tboolean or = test.contains(\"|\");\n\t\tif (and && or) {\n\t\t\tthrow new SumkException(34645465, \"if的test不能同时出现,和|\");\n\t\t}\n\t\treturn createParamExpression(test, matchType, !or);\n\t}\n\n\tprivate static Predicate<Map<String, Object>> createParamExpression(String test, String matchType, boolean and) {\n\t\tString[] ss = and ? test.split(SPLIT_AND) : test.split(SPLIT_OR);\n\t\tList<Predicate<Map<String, Object>>> list = new ArrayList<>(ss.length);\n\t\tfor (String s : ss) {\n\t\t\ts = s.trim();\n\t\t\tif (s.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tSimpleExpression exp = Expressions.createSimpleExpression(s, matchType);\n\t\t\tif (!list.contains(exp)) {\n\t\t\t\tlist.add(exp);\n\t\t\t}\n\t\t}\n\t\treturn Expressions.booleanExpression(list, and);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/SqlXmlBuilderFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.io.InputStream;\nimport java.io.StringReader;\nimport java.nio.charset.StandardCharsets;\nimport java.util.function.Supplier;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\n\nimport org.xml.sax.InputSource;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.util.IOUtil;\n\npublic class SqlXmlBuilderFactory implements Supplier<DocumentBuilder> {\n\tprotected final DocumentBuilderFactory dbf;\n\tprotected String sumkDTD;\n\n\tpublic SqlXmlBuilderFactory() {\n\t\tdbf = DocumentBuilderFactory.newInstance();\n\t\tdbf.setIgnoringComments(true);\n\t\tdbf.setIgnoringElementContentWhitespace(true);\n\t\tdbf.setExpandEntityReferences(false);\n\t\tdbf.setCoalescing(true);\n\t}\n\n\tprivate DocumentBuilder create() throws Exception {\n\n\t\tDocumentBuilder dbd = dbf.newDocumentBuilder();\n\t\tdbd.setEntityResolver((publicId, systemId) -> {\n\t\t\tif (systemId == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString dtd = this.sumkDTD;\n\t\t\tif (dtd == null) {\n\t\t\t\tInputStream in = SqlXmlParser.class.getClassLoader().getResourceAsStream(\"META-INF/sql.dtd\");\n\t\t\t\tif (in == null) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tbyte[] bs = IOUtil.readAllBytes(in, true);\n\t\t\t\tif (bs == null || bs.length == 0) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tdtd = new String(bs, StandardCharsets.UTF_8);\n\t\t\t\tthis.sumkDTD = dtd;\n\t\t\t}\n\t\t\tInputSource source = new InputSource(systemId);\n\t\t\tsource.setCharacterStream(new StringReader(dtd));\n\t\t\tsource.setEncoding(\"UTF-8\");\n\t\t\treturn source;\n\t\t});\n\t\treturn dbd;\n\t}\n\n\t@Override\n\tpublic DocumentBuilder get() {\n\t\ttry {\n\t\t\treturn this.create();\n\t\t} catch (Exception e) {\n\t\t\tLogs.db().error(e.toString(), e);\n\t\t\tthrow new SumkException(7623435, \"创建XmlDocumentBuilder失败\");\n\t\t}\n\t}\n\n\tpublic DocumentBuilderFactory getDocumentBuilderFactory() {\n\t\treturn dbf;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/mapper/SqlXmlParser.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.mapper;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.SAXException;\nimport org.yx.common.expression.MatchType;\nimport org.yx.exception.SumkException;\n\npublic class SqlXmlParser {\n\n\tprivate static final String ID = \"id\";\n\n\tpublic static void parseXml(Map<String, SqlParser> map, DocumentBuilder dbd, String fileName, InputStream in)\n\t\t\tthrows Exception {\n\t\tDocument doc = dbd.parse(in);\n\t\tElement root = doc.getDocumentElement();\n\t\tString namespace = root.getAttribute(\"namespace\");\n\t\tif (namespace != null) {\n\t\t\tnamespace = namespace.trim();\n\t\t\tif (namespace.isEmpty()) {\n\t\t\t\tnamespace = null;\n\t\t\t}\n\t\t}\n\t\tNodeList sqlNodeList = root.getChildNodes();\n\t\tif (sqlNodeList == null || sqlNodeList.getLength() == 0) {\n\t\t\treturn;\n\t\t}\n\t\tint len = sqlNodeList.getLength();\n\t\tNode tmp;\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\ttmp = sqlNodeList.item(i);\n\t\t\tif (!Element.class.isInstance(tmp)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tElement el = (Element) tmp;\n\t\t\tif (!el.hasChildNodes()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tSqlParser parser = compose(parseSqlNode(el.getChildNodes()));\n\t\t\tif (parser != null) {\n\t\t\t\tif (map.putIfAbsent(name(namespace, el.getAttribute(ID)), parser) != null) {\n\t\t\t\t\tthrow new SumkException(1435436,\n\t\t\t\t\t\t\tfileName + \"-\" + name(namespace, el.getAttribute(ID)) + \" is duplicate\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static String name(String namespace, String id) {\n\t\tif (namespace == null) {\n\t\t\treturn id;\n\t\t}\n\t\treturn String.join(\".\", namespace, id);\n\t}\n\n\tprivate static void add(List<SqlParser> list, SqlParser parser) {\n\t\tif (parser != null) {\n\t\t\tlist.add(parser);\n\t\t}\n\t}\n\n\tprivate static Predicate<Map<String, Object>> paramExpression(Element el) {\n\t\treturn SqlParsers.createParamExpression(el.getAttribute(\"test\"),\n\t\t\t\tMatchType.matchTypeOrDefault(el.getAttribute(\"falseby\")));\n\t}\n\n\tprivate static SqlParser compose(List<SqlParser> list) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (list.size() == 1) {\n\t\t\treturn list.get(0);\n\t\t}\n\t\treturn ItemsParser.create(list, JoinerFactory.create(null, null, null));\n\t}\n\n\tprivate static List<SqlParser> parseSqlNode(NodeList sqlNodeList)\n\t\t\tthrows SAXException, IOException, ParserConfigurationException {\n\t\tif (sqlNodeList == null) {\n\t\t\treturn null;\n\t\t}\n\t\tint len = sqlNodeList.getLength();\n\t\tif (len == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tList<SqlParser> list = new ArrayList<>(len);\n\t\tNode tmp;\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\ttmp = sqlNodeList.item(i);\n\t\t\tif (tmp.getNodeType() == Node.TEXT_NODE) {\n\t\t\t\tString sql = tmp.getTextContent();\n\t\t\t\tif (sql == null || (sql = sql.trim()).isEmpty()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tadd(list, PureStringParser.create(sql));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!Element.class.isInstance(tmp)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tElement el = (Element) tmp;\n\t\t\tswitch (el.getTagName()) {\n\t\t\tcase \"if\":\n\t\t\t\tadd(list, IFParser.create(paramExpression(el), compose(parseSqlNode(el.getChildNodes()))));\n\t\t\t\tbreak;\n\t\t\tcase \"ifnot\":\n\t\t\t\tadd(list, IFParser.create(paramExpression(el).negate(), compose(parseSqlNode(el.getChildNodes()))));\n\t\t\t\tbreak;\n\t\t\tcase \"items\":\n\t\t\t\tadd(list, items(el));\n\t\t\t\tbreak;\n\t\t\tcase \"foreach\":\n\t\t\t\tadd(list, forEach(el));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new SumkException(1874546534, el + \"不是有效的tag\");\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate static SqlParser forEach(Element el) {\n\t\tString collection = el.getAttribute(\"collection\");\n\t\tString itemName = el.getAttribute(\"item\");\n\t\tString template = el.getTextContent();\n\t\tJoinerFactory joiner = JoinerFactory.create(el.getAttribute(\"separator\"), el.getAttribute(\"open\"),\n\t\t\t\tel.getAttribute(\"close\"));\n\t\treturn ForeachParser.create(collection, itemName, template, joiner);\n\t}\n\n\tprivate static SqlParser items(Element el) throws SAXException, IOException, ParserConfigurationException {\n\t\tJoinerFactory joiner = JoinerFactory.create(el.getAttribute(\"separator\"), el.getAttribute(\"open\"),\n\t\t\t\tel.getAttribute(\"close\"));\n\t\tList<SqlParser> list = parseSqlNode(el.getChildNodes());\n\t\treturn ItemsParser.create(list, joiner);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/monitor/DBMonitor.java",
    "content": "package org.yx.db.monitor;\n\nimport static org.yx.common.monitor.Monitors.BLANK;\nimport static org.yx.conf.AppInfo.LN;\n\nimport java.util.List;\n\nimport org.yx.common.monitor.MessageProvider;\nimport org.yx.conf.AppInfo;\nimport org.yx.db.conn.DataSourceManager;\nimport org.yx.db.conn.DataSources;\nimport org.yx.db.mapper.NamedExecutor;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.db.sql.PojoMetaHolder;\nimport org.yx.db.sql.VisitCounter;\nimport org.yx.db.visit.SumkStatement;\nimport org.yx.util.StringUtil;\n\npublic class DBMonitor implements MessageProvider {\n\tpublic String dbVisitInfo() {\n\t\tList<PojoMeta> list = PojoMetaHolder.allPojoMeta();\n\t\tStringBuilder sb = new StringBuilder(128);\n\t\tsb.append(\"##sql总执行次数\").append(BLANK).append(SumkStatement.getExecuteCount()).append(BLANK).append(BLANK)\n\t\t\t\t.append(\"SDB执行次数\").append(BLANK).append(NamedExecutor.getExecuteCount()).append(LN);\n\t\tsb.append(\"#tableName\").append(BLANK).append(\"queryCount\").append(BLANK).append(\"modifyCount\").append(BLANK)\n\t\t\t\t.append(\"cacheKeyVisits\").append(BLANK).append(\"cacheKeyHits\").append(AppInfo.LN);\n\t\tlong modify, query, cacheVisit, cacheHit;\n\t\tlong totalModify = 0, totalQuery = 0, totalCacheVisit = 0, totalCacheHit = 0;\n\t\tfor (PojoMeta p : list) {\n\t\t\tVisitCounter c = p.getCounter();\n\t\t\tquery = c.getQueryCount();\n\t\t\tmodify = c.getModifyCount();\n\t\t\tsb.append(p.getTableName()).append(BLANK).append(query).append(BLANK).append(modify).append(BLANK);\n\t\t\ttotalModify += modify;\n\t\t\ttotalQuery += query;\n\t\t\tif (p.isNoCache()) {\n\t\t\t\tsb.append(\"-\").append(BLANK).append(\"-\").append(AppInfo.LN);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcacheVisit = c.getCacheKeyVisits();\n\t\t\tcacheHit = c.getCacheKeyHits();\n\t\t\tsb.append(cacheVisit).append(BLANK).append(cacheHit).append(AppInfo.LN);\n\t\t\ttotalCacheVisit += cacheVisit;\n\t\t\ttotalCacheHit += cacheHit;\n\t\t}\n\t\tsb.append(\"*total*\").append(BLANK).append(totalQuery).append(BLANK).append(totalModify).append(BLANK)\n\t\t\t\t.append(totalCacheVisit).append(BLANK).append(totalCacheHit).append(AppInfo.LN);\n\t\treturn sb.toString();\n\t}\n\n\tpublic String dataSourceStatus(String name) {\n\t\tif (StringUtil.isEmpty(name)) {\n\t\t\treturn null;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(200);\n\t\tif (!DataSources.getManagerSelector().dbNames().contains(name)) {\n\t\t\tsb.append(DataSources.getManagerSelector().dbNames());\n\t\t\treturn sb.toString();\n\t\t}\n\t\tDataSourceManager manager = DataSources.getManager(name);\n\t\tif (manager == null) {\n\t\t\treturn sb.toString();\n\t\t}\n\t\tsb.append(manager.status());\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic Object get(String type, String key, Object param) {\n\t\tif (\"monitor\".equals(type)) {\n\t\t\tif (\"db.cache\".equals(key)) {\n\t\t\t\treturn this.dbVisitInfo();\n\t\t\t}\n\t\t\tif (\"datasource\".equals(key)) {\n\t\t\t\treturn this.dataSourceStatus((String) param);\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/spec/BoxSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.spec;\n\nimport java.util.Objects;\n\nimport org.yx.db.enums.DBType;\nimport org.yx.db.enums.TransactionType;\n\npublic class BoxSpec {\n\tprivate final String value;\n\tprivate final DBType dbType;\n\tprivate final TransactionType transaction;\n\n\tpublic BoxSpec(String value, DBType dbType, TransactionType transaction) {\n\t\tthis.value = Objects.requireNonNull(value);\n\t\tthis.dbType = Objects.requireNonNull(dbType);\n\t\tthis.transaction = Objects.requireNonNull(transaction);\n\t}\n\n\t/**\n\t * @return 数据库字段的名字，不填的话，就是属性名(小写)\n\t */\n\tpublic String value() {\n\t\treturn this.value;\n\t}\n\n\tpublic DBType dbType() {\n\t\treturn this.dbType;\n\t}\n\n\tpublic TransactionType transaction() {\n\t\treturn this.transaction;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/spec/ColumnSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.spec;\n\nimport java.util.Objects;\n\nimport org.yx.db.enums.ColumnType;\n\npublic class ColumnSpec {\n\tprivate final String value;\n\tprivate final ColumnType type;\n\tprivate final byte order;\n\n\tpublic ColumnSpec(String value, ColumnType type, byte order) {\n\t\tthis.value = value;\n\t\tthis.type = Objects.requireNonNull(type);\n\t\tthis.order = order;\n\t}\n\n\t/**\n\t * @return 数据库字段的名字，不填的话，就是属性名(小写)\n\t */\n\tpublic String value() {\n\t\treturn this.value;\n\t}\n\n\tpublic ColumnType type() {\n\t\treturn this.type;\n\t}\n\n\tpublic byte order() {\n\t\treturn this.order;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/spec/DBSpecs.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.spec;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.Objects;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\nimport org.yx.annotation.db.Box;\nimport org.yx.annotation.db.Column;\nimport org.yx.annotation.db.Table;\nimport org.yx.annotation.spec.Specs;\n\npublic class DBSpecs extends Specs {\n\n\tprivate static Function<Class<?>, TableSpec> tableParser = clz -> {\n\t\tTable table = clz.getAnnotation(Table.class);\n\t\tif (table == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new TableSpec(table.value(), table.duration(), table.preInCache(), table.maxHit(), table.cacheType());\n\t};\n\tprivate static BiFunction<Class<?>, Field, ColumnSpec> columnParser = (clz, f) -> {\n\t\tColumn c = f.getAnnotation(Column.class);\n\t\tif (c == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new ColumnSpec(c.value(), c.type(), c.order());\n\t};\n\n\tprivate static Function<Method, BoxSpec> boxParser = m -> {\n\t\tBox c = m.getAnnotation(Box.class);\n\t\tif (c == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new BoxSpec(c.value(), c.dbType(), c.transaction());\n\t};\n\n\tpublic static TableSpec extractTable(Class<?> clz) {\n\t\treturn parse(clz, tableParser);\n\t}\n\n\tpublic static ColumnSpec extractColumn(Class<?> clz, Field f) {\n\t\treturn parse(clz, f, columnParser);\n\t}\n\n\tpublic static BoxSpec extractBox(Method m) {\n\t\treturn parse(m, boxParser);\n\t}\n\n\tpublic static void setBoxParser(Function<Method, BoxSpec> boxParser) {\n\t\tDBSpecs.boxParser = Objects.requireNonNull(boxParser);\n\t}\n\n\tpublic static void setColumnParser(BiFunction<Class<?>, Field, ColumnSpec> columnParser) {\n\t\tDBSpecs.columnParser = Objects.requireNonNull(columnParser);\n\t}\n\n\tpublic static void setTableParser(Function<Class<?>, TableSpec> tableParser) {\n\t\tDBSpecs.tableParser = Objects.requireNonNull(tableParser);\n\t}\n\n\tpublic static Function<Class<?>, TableSpec> getTableParser() {\n\t\treturn tableParser;\n\t}\n\n\tpublic static BiFunction<Class<?>, Field, ColumnSpec> getColumnParser() {\n\t\treturn columnParser;\n\t}\n\n\tpublic static Function<Method, BoxSpec> getBoxParser() {\n\t\treturn boxParser;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/spec/TableSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.spec;\n\nimport java.util.Objects;\n\nimport org.yx.db.enums.CacheType;\n\npublic class TableSpec {\n\tprivate final String value;\n\tprivate final int duration;\n\tprivate final String preInCache;\n\tprivate final int maxHit;\n\tprivate final CacheType cacheType;\n\n\tpublic TableSpec(String value, int duration, String preInCache, int maxHit, CacheType cacheType) {\n\t\tthis.value = value;\n\t\tthis.duration = duration;\n\t\tthis.preInCache = preInCache;\n\t\tthis.maxHit = maxHit;\n\t\tthis.cacheType = Objects.requireNonNull(cacheType);\n\t}\n\n\t/**\n\t * @return 表名。为空时，就是表名，支持#或者?作为通配符\n\t */\n\tpublic String value() {\n\t\treturn value;\n\t}\n\n\t/**\n\t * @return 在缓存中保留的时间,单位秒。0表示使用全局设置，小于0表示不过期\n\t */\n\tpublic int duration() {\n\t\treturn duration;\n\t}\n\n\t/**\n\t * 如果使用cluster，同一张表的DB缓存会在一个slot上，这是为了防止mget、mset出问题\n\t * \n\t * @return 为空使用表名，一般使用默认就好。支持#或者?作为通配符\n\t */\n\tpublic String preInCache() {\n\t\treturn preInCache;\n\t}\n\n\t/**\n\t * @return 访问多少次之后刷新缓存，0表示使用全局默认，小于0表示不刷新\n\t */\n\tpublic int maxHit() {\n\t\treturn maxHit;\n\t}\n\n\t/**\n\t * @return 主键缓存都是SINGLE，外键缓存一般用LIST\n\t */\n\tpublic CacheType cacheType() {\n\t\treturn cacheType;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/AbstractOperationGroup.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.yx.base.ItemJoiner;\n\npublic abstract class AbstractOperationGroup implements CompareOperation {\n\n\tprotected List<CompareOperation> compare;\n\n\tprotected void addOperation(CompareOperation op) {\n\t\tif (op == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (this.compare == null) {\n\t\t\tthis.compare = new ArrayList<>(6);\n\t\t}\n\t\tthis.compare.add(op);\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn compare == null || compare.isEmpty();\n\t}\n\n\tpublic int size() {\n\t\treturn compare == null ? 0 : compare.size();\n\t}\n\n\tCompareOperation get(int index) {\n\t\treturn this.compare.get(index);\n\t}\n\n\tpublic void removeCompares(String key, Operation op) {\n\t\tif (this.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tIterator<CompareOperation> it = this.compare.iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tCompareOperation op0 = it.next();\n\t\t\tif (!(op0 instanceof ColumnOperation)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tColumnOperation cp = (ColumnOperation) op0;\n\t\t\tif (key != null && !key.equals(cp.getName())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (op != null && cp.getType() != op) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tit.remove();\n\t\t}\n\t}\n\n\tprotected CharSequence buildSql(SelectBuilder select, List<Object> paramters, String split) {\n\t\tif (this.isEmpty()) {\n\t\t\treturn \"\";\n\t\t}\n\t\tItemJoiner joiner = ItemJoiner.create(split, \" ( \", \" ) \");\n\t\tfor (CompareOperation op : this.compare) {\n\t\t\tCharSequence cs = op.buildSql(select, paramters);\n\t\t\tif (cs.length() > 0) {\n\t\t\t\tjoiner.item().append(cs);\n\t\t\t}\n\t\t}\n\t\treturn joiner.toCharSequence();\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/AbstractSqlBuilder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.yx.base.sumk.map.ListMap;\nimport org.yx.db.visit.SumkDbVisitor;\nimport org.yx.exception.SumkException;\nimport org.yx.util.BitUtil;\nimport org.yx.util.CollectionUtil;\n\npublic abstract class AbstractSqlBuilder<T> implements SqlBuilder {\n\n\tprotected SumkDbVisitor<T> visitor;\n\n\tprotected Class<?> tableClass;\n\n\tprotected PojoMeta pojoMeta;\n\n\tprotected List<Map<String, Object>> in;\n\n\tprotected int flag;\n\n\tprotected String sub;\n\n\tprotected void sub(String sub) {\n\t\tthis.sub = sub;\n\t\tif (this.pojoMeta != null) {\n\t\t\tthis.pojoMeta = PojoMetaHolder.getPojoMeta(this.pojoMeta, sub);\n\t\t}\n\t}\n\n\tprotected void checkMap(Map<String, ?> map, PojoMeta pm) {\n\t\tif (!this.isOn(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED)) {\n\t\t\treturn;\n\t\t}\n\t\tSet<String> keys = map.keySet();\n\t\tfor (String key : keys) {\n\t\t\tif (pm.getByFieldName(key) == null) {\n\t\t\t\tthrow new SumkException(743257, key + \" is not a field in \" + pm.pojoClz.getName());\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void failIfNotAllowPropertyMiss(String key) {\n\t\tif (!this.isOn(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED)) {\n\t\t\treturn;\n\t\t}\n\t\tString msg = this.pojoMeta == null ? key + \" is not a field\"\n\t\t\t\t: key + \" is not a field in \" + pojoMeta.pojoClz.getName();\n\t\tthrow new SumkException(743257, msg);\n\t}\n\n\tprotected void checkIn() {\n\t\tif (CollectionUtil.isEmpty(this.in)) {\n\t\t\tthrow new SumkException(7345245, \"no conditions\");\n\t\t}\n\t}\n\n\tprotected void _addIn(Object src) {\n\t\tthis._addInByMap(this.populate(src, false));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprotected Map<String, Object> populate(Object src, boolean keepNull) {\n\t\tif (src == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tif (src instanceof Map) {\n\t\t\treturn new ListMap<>((Map<String, Object>) src);\n\t\t}\n\n\t\tif (this.tableClass == null) {\n\t\t\tthis.tableClass = src.getClass();\n\t\t}\n\t\ttry {\n\t\t\treturn this.makeSurePojoMeta().populate(src, keepNull);\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(42534254, e.getMessage(), e);\n\t\t}\n\t}\n\n\tprotected void _addInByMap(Map<String, Object> map) {\n\t\tif (map == null || map.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tif (this.in == null) {\n\t\t\tthis.in = new ArrayList<>();\n\t\t}\n\t\tthis.in.add(map);\n\t}\n\n\tpublic PojoMeta makeSurePojoMeta() {\n\t\tif (this.pojoMeta != null) {\n\t\t\treturn this.pojoMeta;\n\t\t}\n\t\tif (this.tableClass != null) {\n\t\t\tthis.pojoMeta = PojoMetaHolder.getPojoMeta(tableClass, sub);\n\t\t}\n\t\tif (this.pojoMeta == null) {\n\t\t\tthrow new SumkException(7325435, \"please call tableClass(XX.class) first\");\n\t\t}\n\t\treturn this.pojoMeta;\n\t}\n\n\tpublic AbstractSqlBuilder(SumkDbVisitor<T> visitor) {\n\t\tthis.visitor = visitor;\n\t\tthis.flag = DBSettings.flag();\n\t}\n\n\tpublic final boolean isOn(int flagBit) {\n\t\treturn BitUtil.getBit(this.flag, flagBit);\n\t}\n\n\tprotected final void setOnOff(int flagBit, boolean onOff) {\n\t\tthis.flag = BitUtil.setBit(this.flag, flagBit, onOff);\n\t}\n\n\tpublic int flag() {\n\t\treturn this.flag;\n\t}\n\n\tprotected T accept(SumkDbVisitor<T> visitor) {\n\t\ttry {\n\t\t\treturn visitor.visit(this);\n\t\t} catch (Exception e) {\n\t\t\tif (e instanceof SumkException) {\n\t\t\t\tthrow (SumkException) e;\n\t\t\t}\n\t\t\tthrow new SumkException(53172189, e.getMessage(), e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/ColumnMeta.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.lang.reflect.Field;\nimport java.util.Map;\n\nimport org.yx.annotation.doc.Comment;\nimport org.yx.base.Ordered;\nimport org.yx.common.util.kit.TypeConverter;\nimport org.yx.db.enums.ColumnType;\nimport org.yx.db.spec.ColumnSpec;\nimport org.yx.util.StringUtil;\n\npublic final class ColumnMeta implements Comparable<ColumnMeta> {\n\n\tfinal Field field;\n\n\tfinal ColumnType meta;\n\tfinal byte columnOrder;\n\n\tfinal String dbColumn;\n\n\tpublic ColumnMeta(Field field, ColumnSpec c) {\n\t\tthis.field = field;\n\t\tthis.meta = c == null ? ColumnType.NORMAL : c.type();\n\t\tif (c == null) {\n\t\t\tthis.columnOrder = Ordered.DEFAULT_ORDER;\n\t\t} else {\n\t\t\tthis.columnOrder = c.order();\n\t\t}\n\t\tthis.dbColumn = (c == null || StringUtil.isEmpty(c.value()))\n\t\t\t\t? DBNameResolvers.getColumnNameResolver().apply(field.getName())\n\t\t\t\t: c.value();\n\t}\n\n\tpublic boolean isDBID() {\n\t\treturn this.meta.isDbID();\n\t}\n\n\tpublic boolean isCacheID() {\n\t\treturn this.meta.isCacheID();\n\t}\n\n\tpublic Object value(Object owner) throws IllegalArgumentException, IllegalAccessException {\n\t\tif (owner instanceof Map) {\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tMap<String, Object> map = (Map<String, Object>) owner;\n\t\t\treturn map.get(field.getName());\n\t\t}\n\t\treturn field.get(owner);\n\t}\n\n\tpublic void setValue(Object owner, final Object value) throws Exception {\n\t\tif (owner instanceof Map) {\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tMap<String, Object> map = (Map<String, Object>) owner;\n\t\t\tmap.put(field.getName(), value);\n\t\t\treturn;\n\t\t}\n\t\tfield.set(owner, TypeConverter.convert(value, field.getType()));\n\t}\n\n\tpublic String getFieldName() {\n\t\treturn field.getName();\n\t}\n\n\t@Override\n\tpublic int compareTo(ColumnMeta o) {\n\t\tif (this.columnOrder == o.columnOrder) {\n\t\t\treturn Integer.compare(this.meta.order(), o.meta.order());\n\t\t}\n\t\treturn this.columnOrder > o.columnOrder ? 1 : -1;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ColumnMeta [field=\" + field.getName() + \", meta=\" + meta + \", columnOrder=\" + columnOrder\n\t\t\t\t+ \", dbColumn=\" + dbColumn + \"]\";\n\t}\n\n\tpublic Field getField() {\n\t\treturn field;\n\t}\n\n\tpublic ColumnType getMeta() {\n\t\treturn meta;\n\t}\n\n\tpublic int getColumnOrder() {\n\t\treturn columnOrder;\n\t}\n\n\tpublic String getDbColumn() {\n\t\treturn dbColumn;\n\t}\n\n\tpublic String getComment() {\n\t\tComment c = this.field.getAnnotation(Comment.class);\n\t\treturn c == null ? \"\" : c.value();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/ColumnOperation.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.yx.exception.SumkException;\n\npublic class ColumnOperation implements CompareOperation {\n\tprivate final String name;\n\tprivate final Operation type;\n\tprivate final Object value;\n\n\tpublic ColumnOperation(String name, Operation type, Object value) {\n\t\tthis.name = Objects.requireNonNull(name);\n\t\tthis.type = Objects.requireNonNull(type);\n\t\tthis.value = value;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic Operation getType() {\n\t\treturn type;\n\t}\n\n\tpublic Object getValue() {\n\t\treturn value;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn name + type.op + \"?\";\n\t}\n\n\tpublic boolean isSameOperation(ColumnOperation b) {\n\t\treturn this.name.equals(b.name) && this.type == b.type;\n\t}\n\n\t@Override\n\tpublic CharSequence buildSql(SelectBuilder select, List<Object> paramters) {\n\t\tColumnMeta cm = select.pojoMeta.getByFieldName(name);\n\t\tif (cm == null) {\n\t\t\tif (select.isOn(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED)) {\n\t\t\t\tthrow new SumkException(54675234, name + \"这个字段没有在\" + select.pojoMeta.pojoClz.getName() + \"中定义\");\n\t\t\t}\n\t\t\treturn \"\";\n\t\t}\n\t\tStringBuilder sb = new StringBuilder().append(cm.dbColumn);\n\t\tif (value == null) {\n\t\t\tif (type == Operation.EQUAL) {\n\t\t\t\treturn sb.append(\" IS NULL\");\n\t\t\t}\n\t\t\tif (type == Operation.NOT) {\n\t\t\t\treturn sb.append(\" IS NOT NULL\");\n\t\t\t}\n\t\t\tif (!select.isOn(DBFlag.SELECT_COMPARE_ALLOW_NULL)) {\n\t\t\t\tthrow new SumkException(2342423, name + \"的值为null\");\n\t\t\t}\n\t\t\tif (select.isOn(DBFlag.SELECT_COMPARE_IGNORE_NULL)) {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t}\n\t\tsb.append(type.op);\n\t\tif (type == Operation.IN || type == Operation.NOT_IN) {\n\t\t\tsb.append(\"(\");\n\t\t\tboolean first = true;\n\t\t\tfor (Object obj : (Collection<?>) value) {\n\t\t\t\tparamters.add(obj);\n\t\t\t\tif (first) {\n\t\t\t\t\tsb.append('?');\n\t\t\t\t\tfirst = false;\n\t\t\t\t} else {\n\t\t\t\t\tsb.append(\",?\");\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn sb.append(\")\");\n\t\t}\n\n\t\tparamters.add(value);\n\t\treturn sb.append('?');\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/CompareOperation.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.List;\n\npublic interface CompareOperation {\n\t/**\n\t * 生成sql及参数,如果条件为空就返回长度为0的CharSequence，这时候不能对paramters参数有所修改。 允许抛出异常\n\t * \n\t * @param paramters 解析出来的参数要按顺序放在这里。所以这个接口需要按顺序调用\n\t * @return 使用过占位符的sql,返回值不为null。\n\t */\n\tCharSequence buildSql(SelectBuilder select, List<Object> paramters);\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/Count.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.yx.db.mapper.RawExecutor;\nimport org.yx.util.StringUtil;\n\n/**\n * 直接调用select.count()就可以了，一般不需要显式使用这个类\n * \n * @see select\n */\npublic class Count {\n\tprotected final SelectBuilder select;\n\n\tpublic Count(SelectBuilder select) {\n\t\tthis.select = select;\n\t}\n\n\tpublic long execute() {\n\t\tList<Object> paramters = new ArrayList<>(8);\n\t\tPojoMeta pojoMeta = select.makeSurePojoMeta();\n\t\tpojoMeta.getCounter().incrQueryCount();\n\n\t\tObjects.requireNonNull(pojoMeta, \"pojo meta cannot be null\");\n\t\tStringBuilder sql = new StringBuilder();\n\t\tsql.append(\"SELECT count(*) FROM \").append(pojoMeta.getTableName());\n\t\tCharSequence where = select.buildWhere(paramters);\n\t\tif (StringUtil.isNotEmpty(where)) {\n\t\t\tsql.append(\" WHERE \").append(where);\n\t\t}\n\t\treturn RawExecutor.count(sql.toString(), paramters);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/DBFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.Objects;\n\npublic final class DBFactory {\n\tprivate static DBSupplier supplier = new DBSupplierImpl();\n\n\tpublic static void setSupplier(DBSupplier supplier) {\n\t\tDBFactory.supplier = Objects.requireNonNull(supplier);\n\t}\n\n\tpublic static Select select() {\n\t\treturn supplier.select();\n\t}\n\n\tpublic static Insert insert() {\n\t\treturn supplier.insert();\n\t}\n\n\tpublic static Update update() {\n\t\treturn supplier.update();\n\t}\n\n\tpublic static Delete delete() {\n\t\treturn supplier.delete();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/DBFlag.java",
    "content": "package org.yx.db.sql;\n\npublic class DBFlag {\n\n\tpublic static final int FAIL_IF_PROPERTY_NOT_MAPPED = 1;\n\n\tpublic static final int SELECT_FROM_CACHE = 5;\n\n\tpublic static final int SELECT_TO_CACHE = 6;\n\n\tpublic static final int SELECT_ALLOW_EMPTY_WHERE = 7;\n\n\tpublic static final int SELECT_COMPARE_ALLOW_NULL = 8;\n\n\tpublic static final int SELECT_COMPARE_IGNORE_NULL = 9;\n\n\tpublic static final int SELECT_IGNORE_MAX_OFFSET = 10;\n\n\tpublic static final int SELECT_IGNORE_MAX_LIMIT = 11;\n\n\tpublic static final int UPDATE_UPDATE_DBID = 21;\n\tpublic static final int UPDATE_FULL_UPDATE = 22;\n\t/**\n\t * 如果是fullUpdate，并且其它条件也ok。就使用更新缓存的方式，而不是将缓存删除\n\t */\n\tpublic static final int UPDATE_TO_CACHE = 23;\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/DBNameResolvers.java",
    "content": "package org.yx.db.sql;\n\nimport java.util.Objects;\nimport java.util.function.UnaryOperator;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.util.StringUtil;\n\npublic class DBNameResolvers {\n\n\tprivate static final UnaryOperator<String> DEFAULT_RESOLVER = s -> {\n\t\tif (AppInfo.getBoolean(\"sumk.db.name.lowercase\", false)) {\n\t\t\treturn StringUtil.camelToUnderline(s).toLowerCase();\n\t\t}\n\t\treturn StringUtil.camelToUnderline(s);\n\t};\n\n\tprivate static UnaryOperator<String> columnNameResolver = DEFAULT_RESOLVER;\n\n\tprivate static UnaryOperator<String> tableNameResolver = DEFAULT_RESOLVER;\n\tprivate static UnaryOperator<String> cachePrefixResolver = tableName -> \"{\" + tableName + \"}\";\n\n\tpublic static UnaryOperator<String> getColumnNameResolver() {\n\t\treturn columnNameResolver;\n\t}\n\n\tpublic static void setColumnNameResolver(UnaryOperator<String> columnNameResolver) {\n\t\tDBNameResolvers.columnNameResolver = Objects.requireNonNull(columnNameResolver);\n\t}\n\n\tpublic static UnaryOperator<String> getTableNameResolver() {\n\t\treturn tableNameResolver;\n\t}\n\n\tpublic static void setTableNameResolver(UnaryOperator<String> tableNameResolver) {\n\t\tDBNameResolvers.tableNameResolver = Objects.requireNonNull(tableNameResolver);\n\t}\n\n\tpublic static UnaryOperator<String> getCachePrefixResolver() {\n\t\treturn cachePrefixResolver;\n\t}\n\n\tpublic static void setCachePrefixResolver(UnaryOperator<String> cachePrefixResolver) {\n\t\tDBNameResolvers.cachePrefixResolver = Objects.requireNonNull(cachePrefixResolver);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/DBSettings.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.Objects;\nimport java.util.function.IntFunction;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.db.enums.DBType;\nimport org.yx.log.Logs;\nimport org.yx.util.BitUtil;\n\npublic final class DBSettings {\n\tprivate static boolean TO_CACHE;\n\n\tprivate static int FLAG;\n\n\tprivate static int MAX_LOG_PARAM_LENGTH;\n\n\tprivate static int MAX_SINGLEKEY_TO_CACHE;\n\n\tprivate static int MAX_LIMIT;\n\tprivate static int MAX_OFFSET;\n\n\tprivate static int UNION_LOG_TIME;\n\tprivate static boolean UNION_LOG_ENABLE;\n\tprivate static int DEBUG_LOG_SPEND_TIME;\n\tprivate static byte[] PASSWORD_KEY = new byte[] { 121, 111, 117, 116, 111, 110, 103, 108, 117, 97, 110, 64, 115,\n\t\t\t117, 109, 107 };\n\n\tprivate static DBType readType;\n\n\tprivate static int MAX_QUERY_CACHE_SIZE;\n\n\tprivate static SoftDeleteParser softDeleteParser = new SoftDeleteParserImpl();\n\n\tprivate static IntFunction<VisitCounter> visitCounterFactory;\n\n\tpublic static int flag() {\n\t\treturn FLAG;\n\t}\n\n\tpublic static IntFunction<VisitCounter> visitCounterFactory() {\n\t\treturn visitCounterFactory;\n\t}\n\n\tpublic static void setVisitCounterFactory(IntFunction<VisitCounter> factory) {\n\t\tDBSettings.visitCounterFactory = factory;\n\t}\n\n\tpublic static SoftDeleteParser softDeleteParser() {\n\t\treturn softDeleteParser;\n\t}\n\n\tpublic static void setSoftDeleteParser(SoftDeleteParser parser) {\n\t\tDBSettings.softDeleteParser = parser;\n\t}\n\n\tpublic static int maxQueryCacheSize() {\n\t\treturn MAX_QUERY_CACHE_SIZE;\n\t}\n\n\tpublic static DBType readType() {\n\t\treturn readType;\n\t}\n\n\tpublic static int maxSingleKeyToCache() {\n\t\treturn MAX_SINGLEKEY_TO_CACHE;\n\t}\n\n\tpublic static byte[] getPasswordKey() {\n\t\treturn PASSWORD_KEY;\n\t}\n\n\tpublic static void setPasswordKey(byte[] passwordKey) {\n\t\tPASSWORD_KEY = Objects.requireNonNull(passwordKey);\n\t}\n\n\tpublic static int debugLogSpendTime() {\n\t\treturn DEBUG_LOG_SPEND_TIME;\n\t}\n\n\tpublic static int unionLogTime() {\n\t\treturn UNION_LOG_TIME;\n\t}\n\n\tpublic static boolean isUnionLogEnable() {\n\t\treturn UNION_LOG_ENABLE;\n\t}\n\n\tpublic static boolean toCache() {\n\t\treturn TO_CACHE;\n\t}\n\n\tpublic static int MaxLimit() {\n\t\treturn MAX_LIMIT;\n\t}\n\n\tpublic static int MaxOffset() {\n\t\treturn MAX_OFFSET;\n\t}\n\n\tpublic static int maxSqlParamLength() {\n\t\treturn MAX_LOG_PARAM_LENGTH;\n\t}\n\n\tpublic static synchronized void init() {\n\t\tif (DBSettings.readType != null || MAX_LIMIT > 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tDBSettings.FLAG = BitUtil.setBit(DBSettings.FLAG, DBFlag.UPDATE_FULL_UPDATE,\n\t\t\t\tAppInfo.getBoolean(\"sumk.db.update.full\", false));\n\t\tDBSettings.FLAG = BitUtil.setBit(DBSettings.FLAG, DBFlag.UPDATE_UPDATE_DBID,\n\t\t\t\tAppInfo.getBoolean(\"sumk.db.update.dbid\", false));\n\n\t\tAppInfo.addObserver(info -> {\n\t\t\ttry {\n\t\t\t\tTO_CACHE = AppInfo.getBoolean(\"sumk.db.toCache\", true);\n\t\t\t\tMAX_LIMIT = AppInfo.getInt(\"sumk.db.select.max.limit\", 10000);\n\t\t\t\tMAX_OFFSET = AppInfo.getInt(\"sumk.db.select.max.offset\", 10000);\n\t\t\t\tMAX_LOG_PARAM_LENGTH = AppInfo.getInt(\"sumk.sql.param.maxlength\", 5000);\n\t\t\t\tUNION_LOG_TIME = AppInfo.getInt(\"sumk.unionlog.sql.time\", 0);\n\t\t\t\tUNION_LOG_ENABLE = AppInfo.getBoolean(\"sumk.unionlog.sql.enable\", true);\n\t\t\t\tDEBUG_LOG_SPEND_TIME = AppInfo.getInt(\"sumk.sql.debug.spendTime\", 100);\n\t\t\t\tMAX_QUERY_CACHE_SIZE = AppInfo.getInt(\"sumk.select.query.maxsize\", 1000);\n\t\t\t\tMAX_SINGLEKEY_TO_CACHE = AppInfo.getInt(\"sumk.select.singlekey.max.tocache\", 10);\n\t\t\t\tint flag = DBSettings.FLAG;\n\t\t\t\tflag = BitUtil.setBit(flag, DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED,\n\t\t\t\t\t\tAppInfo.getBoolean(\"sumk.db.failIfPropertyNotMapped\", true));\n\t\t\t\tflag = BitUtil.setBit(flag, DBFlag.SELECT_FROM_CACHE, AppInfo.getBoolean(\"sumk.db.fromCache\", true));\n\t\t\t\tflag = BitUtil.setBit(flag, DBFlag.SELECT_TO_CACHE,\n\t\t\t\t\t\tTO_CACHE && AppInfo.getBoolean(\"sumk.db.select.toCache\", true));\n\t\t\t\tflag = BitUtil.setBit(flag, DBFlag.SELECT_COMPARE_IGNORE_NULL,\n\t\t\t\t\t\tAppInfo.getBoolean(\"sumk.db.select.compare.null.ignore\", true));\n\t\t\t\tflag = BitUtil.setBit(flag, DBFlag.SELECT_COMPARE_ALLOW_NULL,\n\t\t\t\t\t\tAppInfo.getBoolean(\"sumk.db.select.compare.null.allow\", true));\n\t\t\t\tflag = BitUtil.setBit(flag, DBFlag.SELECT_IGNORE_MAX_LIMIT,\n\t\t\t\t\t\tAppInfo.getBoolean(\"sumk.db.select.ignore.maxlimit\", false));\n\t\t\t\tflag = BitUtil.setBit(flag, DBFlag.SELECT_IGNORE_MAX_OFFSET,\n\t\t\t\t\t\tAppInfo.getBoolean(\"sumk.db.select.ignore.maxoffset\", false));\n\t\t\t\tflag = BitUtil.setBit(flag, DBFlag.SELECT_ALLOW_EMPTY_WHERE,\n\t\t\t\t\t\tAppInfo.getBoolean(\"sumk.db.select.emptywhere\", false));\n\t\t\t\tflag = BitUtil.setBit(flag, DBFlag.UPDATE_TO_CACHE, AppInfo.getBoolean(\"sumk.db.update.toCache\", true));\n\t\t\t\tDBSettings.FLAG = flag;\n\t\t\t\tLogs.db().info(\"flag : {}\", Integer.toHexString(DBSettings.FLAG));\n\t\t\t} catch (Exception e) {\n\t\t\t\tLogs.db().error(e.getMessage(), e);\n\t\t\t}\n\t\t\tString read = AppInfo.get(\"sumk.db.readtype\", null);\n\t\t\tif (read != null) {\n\t\t\t\ttry {\n\t\t\t\t\tDBSettings.readType = DBType.valueOf(read.toUpperCase());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLogs.db().error(\"sumk.db.readtype配置不正确,\" + e.getMessage(), e);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tDBSettings.readType = DBType.ANY;\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/DBSupplier.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\npublic interface DBSupplier {\n\tInsert insert();\n\n\tUpdate update();\n\n\tSelect select();\n\n\tDelete delete();\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/DBSupplierImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport org.yx.db.visit.Visitors;\n\npublic class DBSupplierImpl implements DBSupplier {\n\n\t@Override\n\tpublic Insert insert() {\n\t\treturn new Insert(Visitors.modifyVisitor);\n\t}\n\n\t@Override\n\tpublic Update update() {\n\t\treturn new Update(Visitors.modifyVisitor);\n\t}\n\n\t@Override\n\tpublic Delete delete() {\n\t\treturn new Delete(Visitors.modifyVisitor);\n\t}\n\n\t@Override\n\tpublic Select select() {\n\t\treturn new Select(Visitors.queryVisitorForORM);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/Delete.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport org.yx.db.visit.SumkDbVisitor;\n\npublic class Delete extends ModifySqlBuilder {\n\n\tpublic Delete(SumkDbVisitor<Integer> visitor) {\n\t\tsuper(visitor);\n\t}\n\n\tpublic Delete failIfPropertyNotMapped(boolean onOff) {\n\t\tthis.setOnOff(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED, onOff);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 删除的条件。如果是map类型，就要设置tableClass<BR>\n\t * 多次执行delete，相互之间是or条件。<BR>\n\t * <B>注意：如果pojo是map类型，那么它的null值是有效条件</B>\n\t * \n\t * @param pojo map或pojo\n\t * @return 当前对象\n\t */\n\tpublic Delete delete(Object pojo) {\n\t\tthis._addIn(pojo);\n\t\treturn this;\n\t}\n\n\tpublic Delete tableClass(Class<?> tableClass) {\n\t\tthis.tableClass = tableClass;\n\t\treturn this;\n\t}\n\n\t/**\n\t * 分表的情况下，设置分区名。这个方法只能调用一次\n\t * \n\t * @param sub 分区名\n\t * @return 当前对象\n\t */\n\tpublic Delete partition(String sub) {\n\t\tsub(sub);\n\t\treturn this;\n\t}\n\n\tpublic MapedSql toMapedSql() throws InstantiationException, IllegalAccessException {\n\t\tthis.checkIn();\n\t\tif (this.pojoMeta.softDelete == null) {\n\t\t\treturn new HardDelete(this).toMapedSql();\n\t\t}\n\t\treturn new SoftDelete(this).toMapedSql();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/EstimateVisitCounter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport org.yx.conf.AppInfo;\n\npublic class EstimateVisitCounter implements VisitCounter {\n\n\tprivate int visitCount;\n\n\tprivate int cacheMeet;\n\n\tprivate int modifyCount;\n\n\tprivate final int interval;\n\n\tprivate int queryCount;\n\n\tprivate int willVisit;\n\n\tpublic EstimateVisitCounter(int interval) {\n\t\tthis.interval = interval > 0 ? interval : AppInfo.getInt(\"sumk.cache.count\", 500);\n\t\tthis.willVisit = this.interval;\n\t}\n\n\t@Override\n\tpublic long getCacheKeyVisits() {\n\t\treturn Integer.toUnsignedLong(visitCount);\n\t}\n\n\t@Override\n\tpublic long getCacheKeyHits() {\n\t\treturn Integer.toUnsignedLong(cacheMeet);\n\t}\n\n\t@Override\n\tpublic boolean willVisitCache(int c) {\n\t\tif (--willVisit <= 0) {\n\t\t\tthis.willVisit = this.interval;\n\t\t\treturn false;\n\t\t}\n\t\tvisitCount += c;\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void incrCacheHit(int c) {\n\t\tcacheMeet += c;\n\t}\n\n\t@Override\n\tpublic long getModifyCount() {\n\t\treturn Integer.toUnsignedLong(this.modifyCount);\n\t}\n\n\t@Override\n\tpublic void incrModifyCount() {\n\t\tthis.modifyCount++;\n\t}\n\n\t@Override\n\tpublic long getQueryCount() {\n\t\treturn Integer.toUnsignedLong(this.queryCount);\n\t}\n\n\t@Override\n\tpublic void incrQueryCount() {\n\t\tthis.queryCount++;\n\t}\n\n\t@Override\n\tpublic int getInterval() {\n\t\treturn this.interval;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/GroupAND.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.List;\n\nimport org.yx.util.CollectionUtil;\n\npublic class GroupAND extends AbstractOperationGroup {\n\n\tpublic static GroupAND create() {\n\t\treturn new GroupAND();\n\t}\n\n\tpublic GroupAND and(String name, Operation type, Object value) {\n\t\treturn and(new ColumnOperation(name, type, value));\n\t}\n\n\tpublic GroupAND and(CompareOperation op) {\n\t\tthis.addOperation(op);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic CharSequence buildSql(SelectBuilder select, List<Object> paramters) {\n\t\treturn this.buildSql(select, paramters, \" AND \");\n\t}\n\n\tGroupAND unmodifyFirstLevel() {\n\t\tif (this.compare != null) {\n\t\t\tthis.compare = CollectionUtil.unmodifyList(this.compare);\n\t\t}\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/GroupOR.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.List;\n\npublic class GroupOR extends AbstractOperationGroup {\n\n\tpublic static GroupOR create() {\n\t\treturn new GroupOR();\n\t}\n\n\tpublic GroupOR or(String name, Operation type, Object value) {\n\t\treturn or(new ColumnOperation(name, type, value));\n\t}\n\n\tpublic GroupOR or(CompareOperation op) {\n\t\tthis.addOperation(op);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic CharSequence buildSql(SelectBuilder select, List<Object> paramters) {\n\t\treturn this.buildSql(select, paramters, \" OR \");\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/InnerDelete.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.yx.base.ItemJoiner;\nimport org.yx.db.event.DeleteEvent;\n\npublic abstract class InnerDelete {\n\tprotected Delete delete;\n\n\tpublic InnerDelete(Delete delete) {\n\t\tthis.delete = delete;\n\t}\n\n\tprotected MapedSql toMapedSql(StringBuilder sb, MapedSql ms) throws InstantiationException, IllegalAccessException {\n\t\tPojoMeta pojoMeta = delete.makeSurePojoMeta();\n\t\tsb.append(\" WHERE \");\n\t\tItemJoiner orItem = new ItemJoiner(\" OR \", null, null);\n\t\tfor (Map<String, Object> oneWhere : delete.in) {\n\t\t\tdelete.checkMap(oneWhere, pojoMeta);\n\t\t\tItemJoiner andItem = new ItemJoiner(\" AND \", \" ( \", \" ) \");\n\t\t\tfor (Entry<String, Object> en : oneWhere.entrySet()) {\n\t\t\t\tColumnMeta fm = pojoMeta.getByFieldName(en.getKey());\n\t\t\t\tif (fm == null) {\n\t\t\t\t\tdelete.failIfNotAllowPropertyMiss(en.getKey());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tObject value = en.getValue();\n\t\t\t\tif (value == null) {\n\t\t\t\t\tandItem.item().append(fm.dbColumn).append(\" IS NULL\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tandItem.item().append(fm.dbColumn).append(\" = ?\");\n\t\t\t\tms.addParam(value);\n\t\t\t}\n\t\t\torItem.item().append(andItem.toCharSequence());\n\t\t}\n\n\t\tsb.append(orItem.toCharSequence());\n\t\tms.sql = sb.toString();\n\t\tms.event = new DeleteEvent(pojoMeta.getTableName(), delete.flag(), delete.in);\n\t\treturn ms;\n\t}\n}\n\nclass HardDelete extends InnerDelete implements SqlBuilder {\n\n\tpublic HardDelete(Delete delete) {\n\t\tsuper(delete);\n\t}\n\n\tpublic MapedSql toMapedSql() throws InstantiationException, IllegalAccessException {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"DELETE FROM \").append(delete.makeSurePojoMeta().getTableName());\n\t\treturn this.toMapedSql(sb, new MapedSql());\n\t}\n\n}\n\nclass SoftDelete extends InnerDelete implements SqlBuilder {\n\n\tpublic SoftDelete(Delete delete) {\n\t\tsuper(delete);\n\t}\n\n\tpublic MapedSql toMapedSql() throws InstantiationException, IllegalAccessException {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tPojoMeta pojoMeta = delete.makeSurePojoMeta();\n\t\tSoftDeleteMeta sm = pojoMeta.softDelete;\n\t\tsb.append(\"UPDATE \").append(pojoMeta.getTableName()).append(\" SET \").append(sm.columnName).append(\" = ? \");\n\t\tMapedSql ms = new MapedSql();\n\t\tms.addParam(sm.inValidValue);\n\t\treturn this.toMapedSql(sb, ms);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/Insert.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.base.ItemJoiner;\nimport org.yx.base.sumk.map.ListMap;\nimport org.yx.common.util.SeqUtil;\nimport org.yx.db.event.InsertEvent;\nimport org.yx.db.visit.SumkDbVisitor;\nimport org.yx.exception.SumkException;\n\npublic class Insert extends ModifySqlBuilder {\n\n\tprotected List<Object> src = new ArrayList<>();\n\n\tpublic Insert(SumkDbVisitor<Integer> visitor) {\n\t\tsuper(visitor);\n\t}\n\n\t/**\n\t * 分表的情况下，设置分区名。这个方法只能调用一次\n\t * \n\t * @param sub 分区名\n\t * @return 当前对象\n\t */\n\tpublic Insert partition(String sub) {\n\t\tsub(sub);\n\t\treturn this;\n\t}\n\n\t/**\n\t * @param pojo 要插入的对象，该对象不会被DB包修改。如果是Map类型，要设置tableClass<BR>\n\t *             但如果对象中主键为null。而且表只有一个主键，并且是Long类型的话，会自动生成一个id，并且赋值到pojo中<br>\n\t * @return 当前对象\n\t */\n\tpublic Insert insert(Object pojo) {\n\t\tthis.src.add(pojo);\n\t\tthis._addIn(pojo);\n\t\treturn this;\n\t}\n\n\tpublic Insert tableClass(Class<?> tableClass) {\n\t\tthis.tableClass = tableClass;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic MapedSql toMapedSql() throws Exception {\n\t\tthis.checkIn();\n\t\treturn batchInsert();\n\t}\n\n\tprotected MapedSql batchInsert() throws Exception {\n\t\tPojoMeta pojoMeta = this.pojoMeta;\n\t\tMapedSql ms = new MapedSql();\n\t\tItemJoiner columns = ItemJoiner.create(\",\", \" ( \", \" ) \");\n\t\tItemJoiner placeholder = ItemJoiner.create(\",\", \" ( \", \" ) \");\n\t\tList<ColumnMeta> fms = pojoMeta.fieldMetas();\n\t\tint recodeSize = in.size();\n\n\t\tboolean softDeleteAndNotProvided = pojoMeta.isSoftDelete() && !pojoMeta.softDelete.fieldProvided;\n\t\tfor (ColumnMeta fm : fms) {\n\t\t\tString name = fm.dbColumn;\n\t\t\tcolumns.item().append(name);\n\t\t\tplaceholder.item().append('?');\n\t\t}\n\t\tif (softDeleteAndNotProvided) {\n\t\t\tString columnName = pojoMeta.softDelete.columnName;\n\t\t\tcolumns.item().append(columnName);\n\t\t\tplaceholder.item().append('?');\n\t\t}\n\t\tStringBuilder sql = new StringBuilder();\n\t\tsql.append(\"INSERT INTO \").append(pojoMeta.getTableName()).append(columns.toCharSequence(true))\n\t\t\t\t.append(\" VALUES \");\n\t\tCharSequence place = placeholder.toCharSequence(true);\n\t\tfor (int i = 0; i < recodeSize; i++) {\n\t\t\tif (i > 0) {\n\t\t\t\tsql.append(',');\n\t\t\t}\n\t\t\tsql.append(place);\n\t\t}\n\t\tms.sql = sql.toString();\n\n\t\tList<Map<String, Object>> cacheList = new ArrayList<>(recodeSize);\n\t\tfor (int i = 0; i < recodeSize; i++) {\n\t\t\tMap<String, Object> pojoMap = this.in.get(i);\n\t\t\tthis.checkMap(pojoMap, pojoMeta);\n\t\t\tMap<String, Object> map = new ListMap<>(pojoMap.size());\n\t\t\tthis.fillSpecialColumns(pojoMap, src.get(i));\n\t\t\tfor (ColumnMeta fm : fms) {\n\t\t\t\tString key = fm.getFieldName();\n\t\t\t\tObject value = pojoMap.get(key);\n\t\t\t\tms.addParam(value);\n\t\t\t\tmap.put(key, value);\n\t\t\t}\n\t\t\tif (map.isEmpty()) {\n\t\t\t\tthrow new SumkException(829341, \"存在无效的待插入记录\");\n\t\t\t}\n\t\t\tcacheList.add(map);\n\t\t\tif (softDeleteAndNotProvided) {\n\t\t\t\tms.addParam(pojoMeta.softDelete.validValue);\n\t\t\t}\n\t\t}\n\n\t\tms.event = new InsertEvent(pojoMeta.getTableName(), flag, cacheList);\n\t\treturn ms;\n\t}\n\n\tprotected void fillSpecialColumns(Map<String, Object> pojoMap, Object srcObject) throws Exception {\n\t\tList<ColumnMeta> idColumns = pojoMeta.getDatabaseIds();\n\t\tif (idColumns.size() != 1) {\n\t\t\treturn;\n\t\t}\n\t\tColumnMeta idColumn = idColumns.get(0);\n\t\tif (idColumn.field.getType() != Long.class) {\n\t\t\treturn;\n\t\t}\n\t\tLong autoId = (Long) idColumn.value(pojoMap);\n\t\tif (autoId == null) {\n\n\t\t\tautoId = SeqUtil.next(\"seq_\".concat(pojoMeta.getTableName()));\n\t\t\tidColumn.setValue(srcObject, autoId);\n\t\t\tidColumn.setValue(pojoMap, autoId);\n\t\t}\n\t\tList<ColumnMeta> createTimes = pojoMeta.createColumns();\n\t\tif (createTimes.size() > 0) {\n\t\t\tDate now = new Date(SeqUtil.getTimeMillis(autoId) / 1000 * 1000);\n\t\t\tfor (ColumnMeta cTime : createTimes) {\n\t\t\t\tcTime.setValue(srcObject, now);\n\t\t\t\tObject time2 = cTime.value(srcObject);\n\t\t\t\tcTime.setValue(pojoMap, time2);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/InsertResult.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.List;\n\npublic class InsertResult {\n\tprivate int effectCount;\n\tprivate List<Long> autoGeneratedKeys;\n\n\tpublic InsertResult(int effectCount, List<Long> autoGeneratedKeys) {\n\t\tthis.effectCount = effectCount;\n\t\tthis.autoGeneratedKeys = autoGeneratedKeys;\n\t}\n\n\tpublic int getEffectCount() {\n\t\treturn effectCount;\n\t}\n\n\tpublic List<Long> getAutoGeneratedKeys() {\n\t\treturn autoGeneratedKeys;\n\t}\n\n\tpublic long getAutoGeneratedKey() {\n\t\tif (autoGeneratedKeys == null || autoGeneratedKeys.isEmpty()) {\n\t\t\treturn -1;\n\t\t}\n\t\tLong row = autoGeneratedKeys.get(0);\n\t\treturn row == null ? -1 : row.longValue();\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/MapedSql.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.yx.base.ItemJoiner;\nimport org.yx.db.event.DBEvent;\n\npublic class MapedSql {\n\n\tpublic MapedSql() {\n\t\tthis.paramters = new ArrayList<>();\n\t}\n\n\tpublic MapedSql(String sql, List<Object> paramters) {\n\t\tthis.sql = sql;\n\t\tthis.paramters = Objects.requireNonNull(paramters);\n\t}\n\n\tString sql;\n\tprivate List<Object> paramters;\n\tDBEvent event;\n\n\tpublic String getSql() {\n\t\treturn sql;\n\t}\n\n\tpublic void setSql(String sql) {\n\t\tthis.sql = sql;\n\t}\n\n\tpublic List<Object> getParamters() {\n\t\treturn paramters;\n\t}\n\n\tpublic void addParam(Object p) {\n\t\tthis.paramters.add(p);\n\t}\n\n\tpublic void addParams(List<Object> ps) {\n\t\tif (ps == null || ps.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tthis.paramters.addAll(ps);\n\t}\n\n\tpublic DBEvent getEvent() {\n\t\treturn event;\n\t}\n\n\tpublic void setEvent(DBEvent event) {\n\t\tthis.event = event;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn sql + \" -- \" + paramters;\n\t}\n\n\tpublic static MapedSql merge(List<MapedSql> mapeds, ItemJoiner joiner) {\n\t\tif (mapeds == null || mapeds.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tList<Object> params = new ArrayList<>();\n\t\tfor (MapedSql maped : mapeds) {\n\t\t\tjoiner.item().append(maped.sql);\n\t\t\tparams.addAll(maped.paramters);\n\t\t}\n\n\t\treturn new MapedSql(joiner.toCharSequence(true).toString(), params);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/MapedSqlBuilder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.yx.db.sql.token.MapValueHandler;\nimport org.yx.db.sql.token.MapedSqlTokenParser;\nimport org.yx.db.sql.token.ReplaceTokenHandler;\nimport org.yx.db.sql.token.StringTokenParser;\nimport org.yx.db.sql.token.VariableTokenHandler;\n\npublic class MapedSqlBuilder implements SqlBuilder {\n\tprivate final String _sql;\n\tprivate final Function<String, Object> valueHandler;\n\n\tpublic MapedSqlBuilder(String sql, Map<String, Object> map) {\n\t\tthis._sql = sql;\n\t\tthis.valueHandler = new MapValueHandler(map);\n\t}\n\n\tpublic MapedSqlBuilder(String sql, Function<String, Object> valueHandler) {\n\t\tthis._sql = sql;\n\t\tthis.valueHandler = valueHandler;\n\t}\n\n\t@Override\n\tpublic MapedSql toMapedSql() throws Exception {\n\t\tString sql = _sql;\n\t\tif (sql.contains(\"${\")) {\n\t\t\tsql = new StringTokenParser(\"${\", \"}\", ReplaceTokenHandler.create(valueHandler)).parse(sql);\n\t\t}\n\t\treturn new MapedSqlTokenParser(\"#{\", \"}\", VariableTokenHandler.create(valueHandler)).parse(sql);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/ModifySqlBuilder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport org.yx.db.visit.SumkDbVisitor;\n\npublic abstract class ModifySqlBuilder extends AbstractSqlBuilder<Integer> {\n\n\tpublic ModifySqlBuilder(SumkDbVisitor<Integer> visitor) {\n\t\tsuper(visitor);\n\t}\n\n\tpublic int execute() {\n\t\tthis.makeSurePojoMeta().getCounter().incrModifyCount();\n\t\treturn this.accept(visitor);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/Operation.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\npublic enum Operation {\n\tBIG(\" > \"), BIG_EQUAL(\" >= \"), LESS(\" < \"), LESS_EQUAL(\" <= \"), LIKE(\" LIKE \"), NOT_LIKE(\" NOT LIKE \"), NOT(\" <> \"),\n\tIN(\" IN \"), NOT_IN(\" NOT IN \"), EQUAL(\" = \");\n\n\tfinal String op;\n\n\tprivate Operation(String op) {\n\t\tthis.op = op;\n\t}\n\n\tpublic String op() {\n\t\treturn op;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/PojoMeta.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Type;\nimport java.sql.Time;\nimport java.time.LocalTime;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\nimport java.util.function.IntFunction;\n\nimport org.yx.annotation.db.AutoCreateTime;\nimport org.yx.annotation.doc.Comment;\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.base.date.TimeUtil;\nimport org.yx.base.sumk.map.ListMap;\nimport org.yx.conf.AppInfo;\nimport org.yx.db.enums.CacheType;\nimport org.yx.db.spec.TableSpec;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.redis.RedisPool;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\npublic final class PojoMeta implements Cloneable {\n\n\tpublic static final String WILDCHAR = \"#\";\n\tprivate static final char KEY_SPLIT = ':';\n\n\t/**\n\t * 数据库表所对应的java pojo类\n\t */\n\tfinal Class<?> pojoClz;\n\tfinal Type pojoArrayClz;\n\tprivate final CacheType cacheType;\n\n\tprivate final Map<String, ColumnMeta> javaNameDict;\n\n\t@NotNull\n\tprivate final List<ColumnMeta> fieldMetas;\n\t@NotNull\n\tfinal List<ColumnMeta> databaseIds;\n\n\t@NotNull\n\tfinal List<ColumnMeta> cacheIDs;\n\n\t@NotNull\n\tprivate final List<ColumnMeta> createColumns;\n\n\tprivate VisitCounter counter;\n\tprivate int ttlSec;\n\n\tprivate String pre;\n\tprivate String tableName;\n\n\tfinal SoftDeleteMeta softDelete;\n\n\tpublic PojoMeta(TableSpec table, List<ColumnMeta> fieldMetas, Class<?> pojoClz) {\n\t\tCacheType tableCacheType = Objects.requireNonNull(table.cacheType());\n\t\tthis.fieldMetas = CollectionUtil.unmodifyList(fieldMetas);\n\t\tthis.pojoClz = pojoClz;\n\t\tList<ColumnMeta> rids = new ArrayList<>();\n\t\tList<ColumnMeta> pids = new ArrayList<>();\n\t\tList<ColumnMeta> ctimes = new ArrayList<>();\n\t\tfor (ColumnMeta m : this.fieldMetas) {\n\t\t\tif (tableCacheType != CacheType.NOCACHE && m.isCacheID()) {\n\t\t\t\trids.add(m);\n\t\t\t}\n\t\t\tif (m.isDBID()) {\n\t\t\t\tpids.add(m);\n\t\t\t}\n\t\t\tif (m.field.isAnnotationPresent(AutoCreateTime.class)) {\n\t\t\t\tif (TimeUtil.isGenericDate(m.field.getType()) && !timeOnly(m.field.getType())) {\n\t\t\t\t\tctimes.add(m);\n\t\t\t\t} else {\n\t\t\t\t\tLogs.db().warn(\"{}.{}的类型{}不是@CreateTime支持的类型\", pojoClz.getSimpleName(), m.field.getName(),\n\t\t\t\t\t\t\tm.field.getType());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.databaseIds = CollectionUtil.unmodifyList(pids);\n\t\tthis.cacheIDs = pids.equals(rids) ? this.databaseIds : CollectionUtil.unmodifyList(rids);\n\t\tthis.cacheType = this.cacheIDs.isEmpty() ? CacheType.NOCACHE : tableCacheType;\n\t\tthis.createColumns = CollectionUtil.unmodifyList(ctimes);\n\t\tthis.softDelete = DBSettings.softDeleteParser().parse(this.pojoClz, this.fieldMetas);\n\t\tthis.parseTable(table);\n\t\tthis.pojoArrayClz = Array.newInstance(this.pojoClz, 0).getClass();\n\t\tHashMap<String, ColumnMeta> dict = new HashMap<>();\n\t\tfor (ColumnMeta cm : this.fieldMetas) {\n\t\t\tdict.put(cm.getFieldName().toLowerCase(), cm);\n\t\t}\n\t\tthis.javaNameDict = dict;\n\t}\n\n\tpublic Type pojoArrayClz() {\n\t\treturn this.pojoArrayClz;\n\t}\n\n\tpublic List<ColumnMeta> fieldMetas() {\n\t\treturn this.fieldMetas;\n\t}\n\n\tpublic ColumnMeta getByColumnDBName(String columnDBName) {\n\t\tfor (ColumnMeta cm : this.fieldMetas) {\n\t\t\tif (cm.dbColumn.equalsIgnoreCase(columnDBName)) {\n\t\t\t\treturn cm;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic ColumnMeta getByFieldName(String fieldName) {\n\t\treturn this.javaNameDict.get(fieldName.toLowerCase());\n\t}\n\n\tpublic boolean isNoCache() {\n\t\treturn cacheType == CacheType.NOCACHE || RedisPool.defaultRedis() == null;\n\t}\n\n\tpublic CacheType cacheType() {\n\t\treturn cacheType;\n\t}\n\n\tpublic boolean isPrimeKeySameWithCache() {\n\t\treturn databaseIds == cacheIDs;\n\t}\n\n\tpublic List<ColumnMeta> getDatabaseIds() {\n\t\treturn databaseIds;\n\t}\n\n\tpublic boolean isSoftDelete() {\n\t\treturn this.softDelete != null;\n\t}\n\n\tpublic VisitCounter getCounter() {\n\t\treturn counter;\n\t}\n\n\t/**\n\t * 重置计数器\n\t * \n\t * @return 旧计数器\n\t */\n\tpublic VisitCounter resetCounter() {\n\t\tVisitCounter old = this.counter;\n\t\tint maxHit = AppInfo.getInt(\"sumk.db.table.cache.maxHit.\" + this.tableName, old.getInterval());\n\t\tIntFunction<VisitCounter> factory = DBSettings.visitCounterFactory();\n\t\tVisitCounter c = factory != null ? factory.apply(maxHit) : new EstimateVisitCounter(maxHit);\n\t\tthis.counter = Objects.requireNonNull(c);\n\t\treturn old;\n\t}\n\n\tpublic int getTtlSec() {\n\t\treturn ttlSec;\n\t}\n\n\tprivate void parseTable(TableSpec table) {\n\t\tint ttl = table.duration();\n\t\tif (ttl > 0) {\n\t\t\tthis.ttlSec = ttl;\n\t\t} else if (ttl == 0) {\n\t\t\tthis.ttlSec = AppInfo.getInt(\"sumk.cache.ttl\", 3600);\n\t\t} else {\n\t\t\tthis.ttlSec = -1;\n\t\t}\n\n\t\tthis.tableName = StringUtil.isEmpty(table.value())\n\t\t\t\t? DBNameResolvers.getTableNameResolver().apply(this.pojoClz.getSimpleName())\n\t\t\t\t: table.value().replace(\"?\", WILDCHAR);\n\t\tString _pre = table.preInCache();\n\n\t\tif (StringUtil.isEmpty(_pre)) {\n\t\t\t_pre = DBNameResolvers.getCachePrefixResolver().apply(this.tableName);\n\t\t}\n\t\tthis.pre = _pre.replace(\"?\", WILDCHAR);\n\n\t\tthis.counter = new EstimateVisitCounter(table.maxHit());\n\t\tthis.resetCounter();\n\t}\n\n\tpublic String getTableName() {\n\t\treturn this.tableName;\n\t}\n\n\tpublic String getPre() {\n\t\treturn this.pre;\n\t}\n\n\tpublic boolean isOnlyCacheID(Object condition) throws IllegalArgumentException, IllegalAccessException {\n\t\tif (this.pojoClz.isInstance(condition)) {\n\t\t\tfor (ColumnMeta m : this.fieldMetas) {\n\t\t\t\tObject v = m.value(condition);\n\t\t\t\tif (m.isCacheID() == (v == null)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (condition instanceof Map) {\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tMap<String, Object> map = (Map<String, Object>) condition;\n\t\t\tif (map.size() != this.cacheIDs.size()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (Entry<String, Object> entry : map.entrySet()) {\n\t\t\t\tif (entry.getValue() == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tString key = entry.getKey();\n\t\t\t\tColumnMeta cm = this.getByFieldName(key);\n\t\t\t\tif (cm == null || !cm.isCacheID()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\n\t}\n\n\tpublic List<ColumnMeta> getCacheIDs() {\n\t\treturn this.cacheIDs;\n\t}\n\n\tpublic Object buildFromDBColumn(Map<String, Object> map) throws Exception {\n\t\tif (map == null) {\n\t\t\treturn null;\n\t\t}\n\t\tObject ret = Loader.newInstance(this.pojoClz);\n\t\tfor (Entry<String, Object> en : map.entrySet()) {\n\t\t\tString key = en.getKey();\n\t\t\tColumnMeta m = this.getByColumnDBName(key);\n\t\t\tif (m == null) {\n\t\t\t\tLogs.db().warn(\"{}数据库字段{}找不到对应的属性\", this.tableName, key);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tm.setValue(ret, en.getValue());\n\t\t}\n\t\treturn ret;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic Map<String, Object> populate(Object source, boolean keepNull)\n\t\t\tthrows InstantiationException, IllegalAccessException {\n\t\tif (source instanceof Map) {\n\t\t\treturn (Map<String, Object>) source;\n\t\t}\n\t\tif (!this.pojoClz.isInstance(source)) {\n\t\t\tthrow new SumkException(548092345,\n\t\t\t\t\tsource.getClass().getName() + \" is not instance of \" + this.pojoClz.getName());\n\t\t}\n\t\tMap<String, Object> map = new ListMap<>(this.fieldMetas.size());\n\t\tfor (ColumnMeta m : this.fieldMetas) {\n\t\t\tObject v = m.value(source);\n\t\t\tif (!keepNull && v == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString name = m.getFieldName();\n\t\t\tmap.put(name, v);\n\t\t}\n\t\treturn map;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic Map<String, Object> populateByDbColumn(Object source, boolean withnull)\n\t\t\tthrows InstantiationException, IllegalAccessException {\n\t\tif (source instanceof Map) {\n\t\t\treturn (Map<String, Object>) source;\n\t\t}\n\t\tif (!this.pojoClz.isInstance(source)) {\n\t\t\tthrow new SumkException(548092346,\n\t\t\t\t\tsource.getClass().getName() + \" is not instance of \" + this.pojoClz.getName());\n\t\t}\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tfor (ColumnMeta m : this.fieldMetas) {\n\t\t\tObject v = m.value(source);\n\t\t\tif (!withnull && v == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString name = m.dbColumn;\n\t\t\tmap.put(name, v);\n\t\t}\n\t\treturn map;\n\t}\n\n\tpublic String getCacheID(Object source, boolean exceptionIfHasNull) throws Exception {\n\t\treturn joinColumns(source, exceptionIfHasNull, this.cacheIDs);\n\t}\n\n\tpublic String getCacheIDWithNULL(Map<String, Object> map) throws Exception {\n\t\tStringBuilder key = new StringBuilder();\n\t\tfor (ColumnMeta m : this.cacheIDs) {\n\t\t\tObject v = map.get(m.getFieldName());\n\t\t\tif (key.length() > 0) {\n\t\t\t\tkey.append(KEY_SPLIT);\n\t\t\t}\n\t\t\tkey.append(v);\n\t\t}\n\t\treturn key.toString();\n\t}\n\n\tprivate String joinColumnsFromMap(Map<String, Object> map, boolean exceptionIfHasNull, List<ColumnMeta> cols) {\n\t\tStringBuilder key = new StringBuilder();\n\t\tfor (ColumnMeta m : cols) {\n\t\t\tObject v = map.get(m.getFieldName());\n\t\t\tif (v == null) {\n\t\t\t\tif (exceptionIfHasNull) {\n\t\t\t\t\tthrow new SumkException(1232142356,\n\t\t\t\t\t\t\tthis.pojoClz.getName() + \": redis key [\" + m.getFieldName() + \"] cannot be null\");\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (key.length() > 0) {\n\t\t\t\tkey.append(KEY_SPLIT);\n\t\t\t}\n\t\t\tkey.append(v);\n\t\t}\n\t\treturn key.toString();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic String joinColumns(Object source, boolean exceptionIfHasNull, List<ColumnMeta> cols) throws Exception {\n\t\tif (source instanceof Map) {\n\t\t\treturn this.joinColumnsFromMap((Map<String, Object>) source, exceptionIfHasNull, cols);\n\t\t}\n\t\tStringBuilder key = new StringBuilder();\n\t\tfor (ColumnMeta m : cols) {\n\t\t\tObject v = m.value(source);\n\t\t\tif (v == null) {\n\t\t\t\tif (exceptionIfHasNull) {\n\t\t\t\t\tthrow new SumkException(1232142356,\n\t\t\t\t\t\t\tthis.pojoClz.getName() + \": value of \" + m.getFieldName() + \" cannot be null\");\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (key.length() > 0) {\n\t\t\t\tkey.append(KEY_SPLIT);\n\t\t\t}\n\t\t\tkey.append(v);\n\t\t}\n\t\treturn key.toString();\n\t}\n\n\tpublic PojoMeta subPojoMeta(String sub) {\n\t\tif (!this.tableName.contains(WILDCHAR)) {\n\t\t\treturn this;\n\t\t}\n\t\tPojoMeta clone;\n\t\ttry {\n\t\t\tclone = (PojoMeta) this.clone();\n\t\t} catch (CloneNotSupportedException e) {\n\t\t\tthrow new SumkException(3456346, e.getMessage());\n\t\t}\n\t\tclone.tableName = subTableName(sub);\n\t\tclone.pre = this.pre.replace(WILDCHAR, sub.toLowerCase());\n\t\treturn clone;\n\t}\n\n\tString subTableName(String sub) {\n\t\treturn this.tableName.replace(WILDCHAR, sub);\n\t}\n\n\tpublic List<ColumnMeta> createColumns() {\n\t\treturn createColumns;\n\t}\n\n\tprivate static boolean timeOnly(Class<?> type) {\n\t\treturn type == Time.class || type == LocalTime.class;\n\t}\n\n\tpublic Class<?> pojoClz() {\n\t\treturn this.pojoClz;\n\t}\n\n\tpublic String getComment() {\n\t\tComment c = this.pojoClz.getAnnotation(Comment.class);\n\t\treturn c == null ? \"\" : c.value();\n\t}\n\n\tpublic SoftDeleteMeta getSoftDelete() {\n\t\treturn softDelete;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/PojoMetaHolder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.yx.db.spec.TableSpec;\nimport org.yx.exception.SumkException;\n\npublic class PojoMetaHolder {\n\n\tprivate static final ConcurrentMap<Class<?>, PojoMeta> pojoMetas = new ConcurrentHashMap<>();\n\tprivate static final ConcurrentMap<String, PojoMeta> tableMetas = new ConcurrentHashMap<>();\n\n\tpublic static PojoMeta getTableMeta(String table) {\n\t\treturn tableMetas.get(table);\n\t}\n\n\tpublic static List<PojoMeta> allPojoMeta() {\n\t\treturn new ArrayList<>(pojoMetas.values());\n\t}\n\n\tpublic static PojoMeta getPojoMeta(PojoMeta pm, String sub) {\n\t\tPojoMeta temp = tableMetas.get(pm.subTableName(sub));\n\t\tif (temp != null) {\n\t\t\treturn temp;\n\t\t}\n\t\tpm = pm.subPojoMeta(sub);\n\t\ttemp = tableMetas.putIfAbsent(pm.getTableName(), pm);\n\t\treturn temp != null ? temp : pm;\n\t}\n\n\tpublic static PojoMeta getPojoMeta(Class<?> clz, String sub) {\n\t\tPojoMeta pm = getPojoMeta(clz);\n\t\tif (pm == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (sub == null || sub.isEmpty()) {\n\t\t\treturn pm;\n\t\t}\n\t\tPojoMeta temp = tableMetas.get(pm.subTableName(sub));\n\t\tif (temp != null) {\n\t\t\treturn temp;\n\t\t}\n\t\tpm = pm.subPojoMeta(sub);\n\t\ttemp = tableMetas.putIfAbsent(pm.getTableName(), pm);\n\t\treturn temp != null ? temp : pm;\n\t}\n\n\tpublic static PojoMeta getPojoMeta(Class<?> clz) {\n\t\tif (clz == null || clz.isInterface() || clz == Object.class) {\n\t\t\treturn null;\n\t\t}\n\t\tClass<?> tmp = clz;\n\t\twhile (tmp != Object.class) {\n\t\t\tPojoMeta m = pojoMetas.get(tmp);\n\t\t\tif (m != null) {\n\t\t\t\treturn m;\n\t\t\t}\n\t\t\ttmp = tmp.getSuperclass();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void register(Class<?> pojoClz, TableSpec table, List<ColumnMeta> columns) {\n\t\tPojoMeta tm = new PojoMeta(table, columns, pojoClz);\n\t\tif (tm.databaseIds.isEmpty()) {\n\t\t\tthrow new SumkException(56456456, pojoClz.getName() + \" has no database primary key\");\n\t\t}\n\t\tpojoMetas.put(pojoClz, tm);\n\t\ttableMetas.put(tm.getTableName(), tm);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/RawSqlBuilder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class RawSqlBuilder implements SqlBuilder {\n\tprivate String _sql;\n\tprivate List<Object> _params;\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic RawSqlBuilder(String sql, Object[] params) {\n\t\tthis._sql = sql;\n\t\tif (params == null || params.length == 0) {\n\t\t\tthis._params = new ArrayList<>();\n\t\t} else if (params.length == 1 && params[0] instanceof List) {\n\t\t\tthis._params = (List<Object>) params[0];\n\t\t} else {\n\t\t\tthis._params = Arrays.asList(params);\n\t\t}\n\t}\n\n\t@Override\n\tpublic MapedSql toMapedSql() throws Exception {\n\t\tMapedSql ms = new MapedSql();\n\t\tms.setSql(_sql);\n\t\tms.addParams(_params);\n\t\treturn ms;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/Select.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport static org.yx.db.sql.Operation.BIG;\nimport static org.yx.db.sql.Operation.BIG_EQUAL;\nimport static org.yx.db.sql.Operation.IN;\nimport static org.yx.db.sql.Operation.LESS;\nimport static org.yx.db.sql.Operation.LESS_EQUAL;\nimport static org.yx.db.sql.Operation.LIKE;\nimport static org.yx.db.sql.Operation.NOT;\nimport static org.yx.db.sql.Operation.NOT_IN;\nimport static org.yx.db.sql.Operation.NOT_LIKE;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.yx.base.sumk.map.ListMap;\nimport org.yx.common.util.kit.Asserts;\nimport org.yx.db.enums.CacheType;\nimport org.yx.db.event.DBEventPublisher;\nimport org.yx.db.event.QueryEvent;\nimport org.yx.db.kit.DBKits;\nimport org.yx.db.visit.Exchange;\nimport org.yx.db.visit.PojoResultHandler;\nimport org.yx.db.visit.ResultHandler;\nimport org.yx.db.visit.SumkDbVisitor;\nimport org.yx.exception.SumkException;\nimport org.yx.util.CollectionUtil;\n\n/**\n * 比较跟整个addEqual是add关系。同一种比较类型，比如less，它的一个key只能设置一次，后设置的会覆盖前面设置的<BR>\n * 比较中用到的key，都是java中的key，大小写敏感.\n */\npublic class Select extends SelectBuilder {\n\tpublic Select(SumkDbVisitor<List<Map<ColumnMeta, Object>>> visitor) {\n\t\tsuper(visitor);\n\t}\n\n\t/**\n\t * @param onOff 如果为true，会验证map参数中，是否存在无效的key，预防开发人员将key写错。默认为true\n\t * @return 当前对象\n\t */\n\tpublic Select failIfPropertyNotMapped(boolean onOff) {\n\t\tthis.setOnOff(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED, onOff);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 物理分表的情况下，设置分区名。这个方法只能调用一次\n\t * \n\t * @param sub 分区名\n\t * @return 当前对象\n\t */\n\tpublic Select partition(String sub) {\n\t\tsub(sub);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 允许不设置where条件\n\t * \n\t * @param empty true表示允许where条件为空\n\t * @return 当前对象\n\t */\n\tpublic Select allowEmptyWhere(boolean empty) {\n\t\tthis.setOnOff(DBFlag.SELECT_ALLOW_EMPTY_WHERE, empty);\n\t\treturn this;\n\t}\n\n\tprotected ResultHandler resultHandler;\n\n\tpublic Select resultHandler(ResultHandler resultHandler) {\n\t\tthis.resultHandler = Objects.requireNonNull(resultHandler);\n\t\treturn this;\n\t}\n\n\tpublic Select compareAllowNull(boolean onOff) {\n\t\tthis.setOnOff(DBFlag.SELECT_COMPARE_ALLOW_NULL, onOff);\n\t\treturn this;\n\t}\n\n\tpublic Select compareIgnoreNull(boolean onOff) {\n\t\tthis.setOnOff(DBFlag.SELECT_COMPARE_IGNORE_NULL, onOff);\n\t\treturn this;\n\t}\n\n\tprotected Select andCompares(Operation op, Object pojo) {\n\t\tMap<String, Object> map = this.populate(pojo, false);\n\t\tif (CollectionUtil.isEmpty(map)) {\n\t\t\treturn this;\n\t\t}\n\t\tfor (Entry<String, Object> en : map.entrySet()) {\n\t\t\tthis.and(op, en.getKey(), en.getValue());\n\t\t}\n\t\treturn this;\n\t}\n\n\tprivate Select and(Operation op, String key, Object value) {\n\t\tthis._compare.and(key, op, value);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置大于,一个key只能设置一次，后设置的会覆盖前面设置的。<BR>\n\t * \n\t * @param key   java字段的名称\n\t * @param value 值\n\t * @return 当前对象\n\t */\n\tpublic Select bigThan(String key, Object value) {\n\t\treturn and(BIG, key, value);\n\t}\n\n\tpublic Select bigOrEqual(String key, Object value) {\n\t\treturn and(BIG_EQUAL, key, value);\n\t}\n\n\tpublic Select lessThan(String key, Object value) {\n\t\treturn and(LESS, key, value);\n\t}\n\n\tpublic Select lessOrEqual(String key, Object value) {\n\t\treturn and(LESS_EQUAL, key, value);\n\t}\n\n\t/**\n\t * like操作，%号要自己添加\n\t * \n\t * @param key   字段名\n\t * @param value 值，不会自动添加%\n\t * @return 当前对象\n\t */\n\tpublic Select like(String key, Object value) {\n\t\treturn and(LIKE, key, value);\n\t}\n\n\tpublic Select notLike(String key, Object value) {\n\t\treturn and(NOT_LIKE, key, value);\n\t}\n\n\t/**\n\t * 增加一些特别的表达式，主要是为了扩展对OR操作符的支持\n\t * \n\t * @param op 一般使用GroupOR.create()来创建一个or表达式\n\t * @return 当前对象\n\t */\n\tpublic Select and(CompareOperation op) {\n\t\tthis._compare.and(op);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 不等于操作\n\t * \n\t * @param key   字段名\n\t * @param value 值，不会自动添加%\n\t * @return 当前对象\n\t */\n\tpublic Select not(String key, Object value) {\n\t\treturn and(NOT, key, value);\n\t}\n\n\t/**\n\t * sql中的in查询\n\t * \n\t * @param key    字段名\n\t * @param values 值列表，不能为空\n\t * @return 当前对象\n\t */\n\tpublic Select in(String key, Collection<?> values) {\n\t\treturn and(IN, key, CollectionUtil.unmodifyList(values));\n\t}\n\n\tpublic Select notIn(String key, Collection<?> values) {\n\t\treturn and(NOT_IN, key, CollectionUtil.unmodifyList(values));\n\t}\n\n\tpublic Select bigThan(Object pojo) {\n\t\treturn andCompares(BIG, pojo);\n\t}\n\n\t/**\n\t * 大于等于\n\t * \n\t * @param pojo 对pojo中所有的kv做大于等于操作\n\t * @return 当前对象\n\t */\n\tpublic Select bigOrEqual(Object pojo) {\n\t\treturn andCompares(BIG_EQUAL, pojo);\n\t}\n\n\tpublic Select lessThan(Object pojo) {\n\t\treturn andCompares(LESS, pojo);\n\t}\n\n\t/**\n\t * 小于或等于\n\t * \n\t * @param pojo 对pojo中所有的kv做小于等于操作\n\t * @return 当前对象\n\t */\n\tpublic Select lessOrEqual(Object pojo) {\n\t\treturn andCompares(LESS_EQUAL, pojo);\n\t}\n\n\tpublic Select like(Object pojo) {\n\t\treturn andCompares(LIKE, pojo);\n\t}\n\n\tpublic Select not(Object pojo) {\n\t\treturn andCompares(NOT, pojo);\n\t}\n\n\t/**\n\t * 根据字段名和判断条件移除所有符合条件的比较。只移除第一级的条件\n\t * \n\t * @param key java字段名，可以为null。null表示所有的字段都要移除\n\t * @param op  比较条件，可以为null。null表示所有的条件都要移除\n\t * @return 当前对象\n\t */\n\tpublic Select removeCompares(String key, Operation op) {\n\t\tthis._compare.removeCompares(key, op);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 升序排列。asc和desc的调用顺序决定了在sql中出现的顺序。 此方法可以调用多次\n\t * \n\t * @param field 升序字段\n\t * @return 当前对象\n\t */\n\tpublic Select orderByAsc(String field) {\n\t\treturn this.addOrderBy(field, false);\n\t}\n\n\tprotected Select addOrderBy(String name, boolean desc) {\n\t\tif (this.orderby == null) {\n\t\t\tthis.orderby = new ArrayList<>(2);\n\t\t}\n\t\tthis.orderby.add(new Order(name, desc));\n\t\treturn this;\n\t}\n\n\t/**\n\t * 增加降序排列\n\t * \n\t * @param field 降序字段\n\t * @return 当前对象\n\t */\n\tpublic Select orderByDesc(String field) {\n\t\treturn this.addOrderBy(field, true);\n\t}\n\n\t/**\n\t * 设置查询的便宜量，从0开始。\n\t * \n\t * @param offset from的位置\n\t * @return 当前对象\n\t */\n\tpublic Select offset(int offset) {\n\t\tif (this.isOn(DBFlag.SELECT_IGNORE_MAX_OFFSET)) {\n\t\t\tif (offset < 0 || offset > DBSettings.MaxOffset()) {\n\t\t\t\tthrow new SumkException(235345347,\n\t\t\t\t\t\t\"offset需要在0-\" + DBSettings.MaxOffset() + \"之间,通过ignoreMaxOffset(true)可以取消这个限制\");\n\t\t\t}\n\t\t}\n\t\tthis.offset = offset;\n\t\treturn this;\n\t}\n\n\t/**\n\t * \n\t * @param limit 返回的最大条数。\n\t * @return 当前对象\n\t */\n\tpublic Select limit(int limit) {\n\t\tif (this.isOn(DBFlag.SELECT_IGNORE_MAX_LIMIT)) {\n\t\t\tif (limit <= 0 || limit > DBSettings.MaxLimit()) {\n\t\t\t\tthrow new SumkException(235345346,\n\t\t\t\t\t\t\"limit需要在1-\" + DBSettings.MaxLimit() + \"之间,通过ignoreMaxLimit(true)可以取消这个限制\");\n\t\t\t}\n\t\t}\n\t\tthis.limit = limit;\n\t\treturn this;\n\t}\n\n\t/**\n\t * \n\t * @param columns 设置查询放回的列，列名是java中的字段名。如果不设，将返回所有的字段\n\t * @return 当前对象\n\t */\n\tpublic Select selectColumns(String... columns) {\n\t\tif (columns == null || columns.length == 0) {\n\t\t\tthis.selectColumns = null;\n\t\t\treturn this;\n\t\t}\n\t\tthis.selectColumns = CollectionUtil.unmodifyList(columns);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 如果为false，就不会从缓存中加载数据\n\t * \n\t * @param fromCache 默认为true。sumk.sql.fromCache=false可以将全局参数设为false\n\t * @return 当前对象\n\t */\n\tpublic Select fromCache(boolean fromCache) {\n\t\tthis.setOnOff(DBFlag.SELECT_FROM_CACHE, fromCache);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 如果为false，查出的结果将不会用于更新缓存\n\t * \n\t * @param toCache 该参数设为true的实际意义不大\n\t * @return 当前对象\n\t */\n\tpublic Select toCache(boolean toCache) {\n\t\tthis.setOnOff(DBFlag.SELECT_TO_CACHE, toCache);\n\t\treturn this;\n\t}\n\n\tpublic Select ignoreMaxLimit(boolean on) {\n\t\tthis.setOnOff(DBFlag.SELECT_IGNORE_MAX_LIMIT, on);\n\t\treturn this;\n\t}\n\n\tpublic Select ignoreMaxOffset(boolean on) {\n\t\tthis.setOnOff(DBFlag.SELECT_IGNORE_MAX_OFFSET, on);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置相等的条件。本方法可以被多次执行。 src中的各个条件是and关系。不同src之间是or关系<BR>\n\t * <B>注意：如果pojo是map类型，那么它的null值是有效条件</B>\n\t * \n\t * @param src map或pojo类型。\n\t * @return 当前对象\n\t */\n\tpublic Select addEqual(Object src) {\n\t\tthis._addIn(src);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 各个addEqual之间的条件是OR，如果要组装AND条件，请用addEqual(Object src)\n\t * \n\t * @param field 字段名\n\t * @param value 要查询的条件的值\n\t * @return 当前对象\n\t */\n\tpublic Select addEqual(String field, Object value) {\n\t\tthis._addInByMap(Collections.singletonMap(field, value));\n\t\treturn this;\n\t}\n\n\t/**\n\t * 通过数据库主键列表查询主键，单主键ids就只有一个，多主键就传入多个 <B>注意：调用本方法之前，要确保调用过tableClass()方法</B>\n\t * \n\t * @param ids id列表，顺序跟pojo中定义的一致(按order顺序或书写顺序)\n\t * @return 注意：调用本方法之前，要确保调用过tableClass()方法\n\t */\n\tpublic Select byDatabaseId(Object... ids) {\n\t\treturn byId(true, ids);\n\t}\n\n\t/**\n\t * 通过redis主键列表查询主键，单主键ids就只有一个，多主键就传入多个<BR>\n\t * 所有id属于同一条记录，如果要使用单主键的in查询，请用本类的in方法<BR>\n\t * <B>注意：调用本方法之前，要确保调用过tableClass()方法</B>\n\t * \n\t * @param ids id列表，顺序跟pojo中定义的一致（按order顺序或书写顺序）\n\t * @return 当前对象\n\t * \n\t */\n\tpublic Select byCacheId(Object... ids) {\n\t\treturn byId(false, ids);\n\t}\n\n\tprotected Select byId(boolean databaseId, Object... ids) {\n\t\tif (ids == null || ids.length == 0) {\n\t\t\treturn this;\n\t\t}\n\t\tmakeSurePojoMeta();\n\t\tList<ColumnMeta> cms = databaseId ? this.pojoMeta.getDatabaseIds() : this.pojoMeta.getCacheIDs();\n\t\tAsserts.requireTrue(cms != null && cms.size() == ids.length, pojoMeta.getTableName() + \"没有设置主键，或者主键个数跟参数个数不一致\");\n\t\tMap<String, Object> map = new ListMap<>(cms.size());\n\t\tfor (int i = 0; i < ids.length; i++) {\n\t\t\tmap.put(cms.get(i).getFieldName(), ids[i]);\n\t\t}\n\t\t_addInByMap(map);\n\t\treturn this;\n\t}\n\n\tpublic Select tableClass(Class<?> tableClass) {\n\t\tthis.tableClass = tableClass;\n\t\treturn this;\n\t}\n\n\tprotected ResultHandler resultHandler() {\n\t\treturn this.resultHandler == null ? PojoResultHandler.handler : this.resultHandler;\n\t}\n\n\tprivate boolean isCompareOnlyCacheId() {\n\t\tif (_compare.size() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\tCompareOperation c0 = _compare.get(0);\n\t\tif (!(c0 instanceof ColumnOperation)) {\n\t\t\treturn false;\n\t\t}\n\t\tColumnOperation cp = (ColumnOperation) c0;\n\t\treturn cp.getType() == IN && cp.getName().equals(this.pojoMeta.cacheIDs.get(0).field.getName());\n\t}\n\n\tprotected boolean canUseInCache() {\n\t\treturn this.pojoMeta.cacheIDs.size() == 1 && CollectionUtil.isEmpty(this.in)\n\t\t\t\t&& this.pojoMeta.cacheType() == CacheType.SINGLE && isCompareOnlyCacheId();\n\t}\n\n\tprotected <T> List<T> queryFromCache(Exchange exchange) throws Exception {\n\t\tif (CollectionUtil.isEmpty(this.in) && this._compare.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tboolean fromCache = this.isOn(DBFlag.SELECT_FROM_CACHE);\n\t\tif (!(fromCache && this.orderby == null && this.offset == 0 && !pojoMeta.isNoCache())) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString singleKeyName = this.canUseInCache() ? this.pojoMeta.cacheIDs.get(0).field.getName() : null;\n\t\tif (singleKeyName != null) {\n\t\t\tColumnOperation cp = (ColumnOperation) _compare.get(0);\n\t\t\tList<?> vs = (List<?>) cp.getValue();\n\t\t\tList<Map<String, Object>> newIN = new ArrayList<>(vs.size());\n\t\t\tfor (Object v : vs) {\n\t\t\t\tnewIN.add(Collections.singletonMap(singleKeyName, v));\n\t\t\t}\n\t\t\texchange.setLeftIn(newIN);\n\t\t} else if (_compare.isEmpty()) {\n\t\t\texchange.setLeftIn(this.in);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<T> list = new ArrayList<>();\n\t\texchange.findFromCache(pojoMeta);\n\t\tif (exchange.getData() != null && exchange.getData().size() > 0) {\n\t\t\tList<T> tmp = this.resultHandler().parseFromJson(pojoMeta, exchange.getData(),\n\t\t\t\t\tCollectionUtil.unmodifyList(this.selectColumns));\n\t\t\tif (tmp != null && tmp.size() > 0) {\n\t\t\t\tlist.addAll(tmp);\n\t\t\t}\n\t\t}\n\t\tList<Map<String, Object>> left = exchange.getLeftIn();\n\n\t\tif (left == null) {\n\t\t\treturn list;\n\t\t}\n\n\t\tif (singleKeyName != null) {\n\t\t\tList<Object> vs = new ArrayList<>(left.size());\n\t\t\tfor (Map<String, Object> m : left) {\n\t\t\t\tvs.add(m.get(singleKeyName));\n\t\t\t}\n\t\t\tthis._compare = GroupAND.create().and(singleKeyName, IN, vs);\n\t\t} else {\n\t\t\tthis.in = left;\n\t\t}\n\t\treturn list;\n\t}\n\n\tprotected final void validSelectColumns() {\n\t\tif (this.selectColumns == null || this.selectColumns.isEmpty()) {\n\t\t\tthis.selectColumns = null;\n\t\t\treturn;\n\t\t}\n\t\tList<String> list = new ArrayList<>(this.selectColumns.size());\n\t\tfor (String filedName : selectColumns) {\n\t\t\tColumnMeta cm = pojoMeta.getByFieldName(filedName);\n\t\t\tif (cm == null) {\n\t\t\t\tthis.failIfNotAllowPropertyMiss(filedName);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.add(cm.getFieldName());\n\t\t}\n\t\tthis.selectColumns = list.isEmpty() ? null : list;\n\t}\n\n\tpublic <T> List<T> queryList() {\n\n\t\tfinal List<Map<String, Object>> origin = this.in = CollectionUtil.unmodifyList(this.in);\n\t\tfinal GroupAND orginCompare = this._compare.unmodifyFirstLevel();\n\t\ttry {\n\t\t\tmakeSurePojoMeta().getCounter().incrQueryCount();\n\t\t\tthis.validSelectColumns();\n\t\t\tExchange exchange = new Exchange();\n\t\t\tList<T> list = this.queryFromCache(exchange);\n\t\t\tif (list != null && CollectionUtil.isEmpty(exchange.getLeftIn())) {\n\t\t\t\treturn list;\n\t\t\t}\n\t\t\tboolean canUseCache = true;\n\t\t\tif (list == null) {\n\t\t\t\tlist = Collections.emptyList();\n\t\t\t\tcanUseCache = false;\n\t\t\t}\n\t\t\tList<T> dbData = this.resultHandler().parse(pojoMeta, this.accept(visitor));\n\t\t\tif (dbData == null || dbData.isEmpty()) {\n\t\t\t\treturn list;\n\t\t\t}\n\t\t\tlist = merge(list, dbData);\n\t\t\tList<Map<String, Object>> eventIn = canUseCache ? exchange.getLeftIn() : this.in;\n\n\t\t\tif (this.isOn(DBFlag.SELECT_TO_CACHE) && selectColumns == null && this.offset <= 0\n\t\t\t\t\t&& (canUseCache || this._compare.isEmpty()) && (limit <= 0 || limit >= DBSettings.MaxLimit())\n\t\t\t\t\t&& CollectionUtil.isNotEmpty(eventIn) && dbData.size() < DBSettings.maxQueryCacheSize()) {\n\n\t\t\t\tQueryEvent event = new QueryEvent(this.pojoMeta.getTableName());\n\t\t\t\tevent.setIn(eventIn);\n\t\t\t\tevent.setResult(dbData);\n\t\t\t\tDBEventPublisher.publishQuery(event);\n\t\t\t}\n\t\t\tif (this.limit > 0 && list.size() > this.limit) {\n\t\t\t\treturn list.subList(0, this.limit);\n\t\t\t}\n\t\t\treturn list;\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t} finally {\n\t\t\tthis.in = origin;\n\t\t\tthis._compare = orginCompare;\n\t\t}\n\t}\n\n\tprotected <T> List<T> merge(List<T> cacheList, List<T> dbList) throws Exception {\n\t\tif (cacheList.isEmpty()) {\n\t\t\treturn dbList;\n\t\t}\n\t\tList<T> ret = new ArrayList<>(cacheList.size() + dbList.size());\n\t\tret.addAll(dbList);\n\t\tSet<String> keys = new HashSet<>();\n\t\tPojoMeta pm = this.pojoMeta;\n\t\tList<ColumnMeta> columns = pm.databaseIds;\n\t\tfor (T t : dbList) {\n\t\t\tString key = pm.joinColumns(t, false, columns);\n\t\t\tif (key != null) {\n\t\t\t\tkeys.add(key);\n\t\t\t}\n\t\t}\n\t\tfor (T t : cacheList) {\n\t\t\tString key = pm.joinColumns(t, false, columns);\n\t\t\tif (key == null || keys.add(key)) {\n\t\t\t\tret.add(t);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\tpublic <T> T queryOne() {\n\t\treturn DBKits.queryOne(this.queryList());\n\t}\n\n\t/**\n\t * 根据select的条件，查询符合条件的记录数。其中offset、limit、order by属性被过滤掉<BR>\n\t * 这个方法可以在select执行前调用，也可以在select执行后调用\n\t * \n\t * @return 符合条件的数据库记录数\n\t */\n\tpublic long count() {\n\t\treturn new Count(this).execute();\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/SelectBuilder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.StringJoiner;\n\nimport org.yx.base.ItemJoiner;\nimport org.yx.db.visit.SumkDbVisitor;\nimport org.yx.exception.SumkException;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic class SelectBuilder extends AbstractSqlBuilder<List<Map<ColumnMeta, Object>>> {\n\n\tprotected List<String> selectColumns;\n\n\tprotected GroupAND _compare = GroupAND.create();\n\n\tprotected List<Order> orderby;\n\n\tprotected int offset;\n\n\tprotected int limit;\n\n\tpublic SelectBuilder(SumkDbVisitor<List<Map<ColumnMeta, Object>>> visitor) {\n\t\tsuper(visitor);\n\t\tthis.limit = DBSettings.MaxLimit();\n\t}\n\n\t/**\n\t * 这个方法用于两个地方，一个是框架内部调用，一旦外部调用有可能影响最终结果 另一个是开发调试的时候，调用本方法查看最终的sql，这时候它不需要放在事务中\n\t * \n\t * @throws Exception 异常信息\n\t */\n\t@Override\n\tpublic MapedSql toMapedSql() throws Exception {\n\t\tmakeSurePojoMeta();\n\t\tList<Object> paramters = new ArrayList<>(10);\n\t\tStringBuilder sql = new StringBuilder(32);\n\t\tsql.append(\"SELECT \").append(this.buildField()).append(\" FROM \").append(this.pojoMeta.getTableName());\n\t\tCharSequence where = this.buildWhere(paramters);\n\t\tif (StringUtil.isNotEmpty(where)) {\n\t\t\tsql.append(\" WHERE \").append(where);\n\t\t}\n\t\tCharSequence order = buildOrder();\n\t\tif (StringUtil.isNotEmpty(order)) {\n\t\t\tsql.append(\" ORDER BY \").append(order);\n\t\t}\n\t\tbuildLimitAndOffset(sql);\n\t\treturn new MapedSql(sql.toString(), paramters);\n\t}\n\n\t/**\n\t * 组装分页，也就是offset和limit\n\t * \n\t * @param sql 已组装出来的sql\n\t */\n\tprotected void buildLimitAndOffset(StringBuilder sql) {\n\n\t\tif (limit > 0) {\n\t\t\tsql.append(\" LIMIT \").append(this.limit);\n\t\t}\n\t\tif (offset > 0) {\n\t\t\tsql.append(\" OFFSET \").append(this.offset);\n\t\t}\n\t}\n\n\tprotected CharSequence buildOrder() {\n\t\tif (CollectionUtil.isEmpty(this.orderby)) {\n\t\t\treturn null;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (Order order : this.orderby) {\n\t\t\tif (sb.length() > 0) {\n\t\t\t\tsb.append(',');\n\t\t\t}\n\t\t\tsb.append(order.toString(this.pojoMeta));\n\t\t}\n\t\treturn sb;\n\t}\n\n\tprotected CharSequence buildField() {\n\t\tPojoMeta pojoMeta = this.pojoMeta;\n\t\tStringJoiner sj = new StringJoiner(\",\");\n\t\tif (this.selectColumns != null && this.selectColumns.size() > 0) {\n\t\t\tfor (String filedName : this.selectColumns) {\n\t\t\t\tColumnMeta cm = pojoMeta.getByFieldName(filedName);\n\t\t\t\tif (cm == null) {\n\t\t\t\t\tthis.failIfNotAllowPropertyMiss(filedName);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tsj.add(cm.dbColumn);\n\t\t\t}\n\t\t\treturn sj.toString();\n\t\t}\n\t\tfor (ColumnMeta cm : pojoMeta.fieldMetas()) {\n\t\t\tsj.add(cm.dbColumn);\n\t\t}\n\t\treturn sj.toString();\n\t}\n\n\tprotected CharSequence buildWhere(List<Object> paramters) {\n\t\tItemJoiner joiner = new ItemJoiner(\" AND \", null, null);\n\t\tjoiner.appendNotEmptyItem(buildEquals(paramters)).appendNotEmptyItem(buildCompare(paramters));\n\t\tif (joiner.isEmpty() && !this.isOn(DBFlag.SELECT_ALLOW_EMPTY_WHERE)) {\n\t\t\tthrow new SumkException(63254325, \"empty where\");\n\t\t}\n\t\treturn joiner.appendNotEmptyItem(buildValid(paramters)).toCharSequence();\n\t}\n\n\tprivate CharSequence buildValid(List<Object> paramters) {\n\t\tSoftDeleteMeta softDelete = this.pojoMeta.softDelete;\n\t\tif (softDelete == null) {\n\t\t\treturn null;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif (softDelete.equalValid) {\n\t\t\tsb.append(softDelete.columnName).append(\" = ?\");\n\t\t\tparamters.add(softDelete.validValue);\n\t\t} else {\n\t\t\tsb.append(softDelete.columnName).append(\" <> ?\");\n\t\t\tparamters.add(softDelete.inValidValue);\n\t\t}\n\t\treturn sb;\n\t}\n\n\tprivate CharSequence buildCompare(List<Object> paramters) {\n\t\treturn this._compare.buildSql(this, paramters);\n\t}\n\n\tprivate CharSequence buildEquals(List<Object> paramters) {\n\t\tif (this.in == null || this.in.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tItemJoiner joiner = ItemJoiner.create(\" OR \", \" ( \", \" ) \");\n\t\tList<Map<String, Object>> list = this.in;\n\t\tfor (Map<String, Object> map : list) {\n\t\t\tCharSequence sub = this.parseEqual(map, paramters);\n\t\t\tif (sub == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tjoiner.item().append(sub);\n\t\t}\n\t\treturn joiner.toCharSequence();\n\t}\n\n\tprivate CharSequence parseEqual(Map<String, Object> src, List<Object> paramters) {\n\t\tif (CollectionUtil.isEmpty(src)) {\n\t\t\treturn null;\n\t\t}\n\t\tItemJoiner joiner = ItemJoiner.create(\" AND \", \" ( \", \" ) \");\n\t\tfor (Entry<String, Object> entry : src.entrySet()) {\n\t\t\tString filedName = entry.getKey();\n\t\t\tObject v = entry.getValue();\n\n\t\t\tColumnMeta cm = pojoMeta.getByFieldName(filedName);\n\t\t\tif (cm == null) {\n\t\t\t\tif (this.isOn(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED)) {\n\t\t\t\t\tthrow new SumkException(7331234, filedName + \"这个字段没有在java的pojo类中定义\");\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (v == null) {\n\t\t\t\tjoiner.item().append(cm.dbColumn).append(\" IS NULL\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tjoiner.item().append(cm.dbColumn).append(\" = ?\");\n\t\t\tparamters.add(v);\n\t\t}\n\n\t\treturn joiner.toCharSequence();\n\t}\n\n\tprotected static class Order {\n\n\t\tfinal String name;\n\n\t\tfinal boolean desc;\n\n\t\tpublic Order(String name, boolean desc) {\n\t\t\tthis.name = name;\n\t\t\tthis.desc = desc;\n\t\t}\n\n\t\tpublic String toString(PojoMeta pm) {\n\t\t\tColumnMeta cm = pm.getByFieldName(name);\n\t\t\tif (cm == null) {\n\t\t\t\tthrow new SumkException(4532018, \"排序字段\" + name + \"不在\" + pm.pojoClz.getName() + \"字段中\");\n\t\t\t}\n\t\t\tString dbName = cm.dbColumn;\n\t\t\tif (desc) {\n\t\t\t\treturn dbName + \" desc\";\n\t\t\t}\n\t\t\treturn dbName;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/SoftDeleteMeta.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\npublic final class SoftDeleteMeta {\n\tfinal String columnName;\n\tfinal Object validValue;\n\tfinal Object inValidValue;\n\n\tfinal boolean fieldProvided;\n\n\tfinal boolean equalValid;\n\n\tpublic SoftDeleteMeta(String columnName, Object validValue, Object inValidValue, boolean equal,\n\t\t\tboolean fieldProvided) {\n\t\tthis.columnName = columnName;\n\t\tthis.validValue = validValue;\n\t\tthis.inValidValue = inValidValue;\n\t\tthis.equalValid = equal;\n\t\tthis.fieldProvided = fieldProvided;\n\t}\n\n\tpublic String getColumnName() {\n\t\treturn columnName;\n\t}\n\n\tpublic Object getValidValue() {\n\t\treturn validValue;\n\t}\n\n\tpublic Object getInValidValue() {\n\t\treturn inValidValue;\n\t}\n\n\tpublic boolean isEqualValid() {\n\t\treturn equalValid;\n\t}\n\n\tpublic boolean isFieldProvided() {\n\t\treturn fieldProvided;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/SoftDeleteParser.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.List;\n\npublic interface SoftDeleteParser {\n\n\tSoftDeleteMeta parse(Class<?> pojoClz, List<ColumnMeta> fieldMetas);\n}"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/SoftDeleteParserImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.List;\n\nimport org.yx.annotation.db.SoftDelete;\nimport org.yx.db.enums.ValidRecord;\nimport org.yx.exception.SumkException;\n\npublic class SoftDeleteParserImpl implements SoftDeleteParser {\n\n\tpublic Object parseValue(Class<?> type, String value) {\n\t\tif (type == String.class) {\n\t\t\treturn value;\n\t\t}\n\t\tif (Integer.class == type || int.class == type) {\n\t\t\treturn Integer.valueOf(value);\n\t\t}\n\t\tif (Byte.class == type || byte.class == type) {\n\t\t\treturn Byte.valueOf(value);\n\t\t}\n\t\tif (Short.class == type || short.class == type) {\n\t\t\treturn Short.valueOf(value);\n\t\t}\n\t\tif (Long.class == type || long.class == type) {\n\t\t\treturn Long.valueOf(value);\n\t\t}\n\t\tif (Double.class == type || double.class == type) {\n\t\t\treturn Double.valueOf(value);\n\t\t}\n\t\tif (Float.class == type || float.class == type) {\n\t\t\treturn Float.valueOf(value);\n\t\t}\n\t\tthrow new SumkException(6234267, type.getName() + \" is not supported by soft delete\");\n\t}\n\n\t@Override\n\tpublic SoftDeleteMeta parse(Class<?> pojoClz, List<ColumnMeta> fieldMetas) {\n\t\tSoftDelete sd = pojoClz.getAnnotation(SoftDelete.class);\n\t\tif (sd == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString name = sd.value();\n\t\tboolean provided = false;\n\t\tfor (ColumnMeta f : fieldMetas) {\n\t\t\tif (name.equalsIgnoreCase(f.getFieldName())) {\n\t\t\t\tprovided = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (sd.type() == Boolean.class || sd.type() == boolean.class) {\n\t\t\treturn new SoftDeleteMeta(sd.value(), Boolean.TRUE, Boolean.FALSE, true, provided);\n\t\t}\n\t\tboolean equal = sd.whatIsValid() == ValidRecord.EQUAL_VALID;\n\t\treturn new SoftDeleteMeta(sd.value(), parseValue(sd.type(), sd.validValue()),\n\t\t\t\tparseValue(sd.type(), sd.inValidValue()), equal, provided);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/SqlBuilder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\npublic interface SqlBuilder {\n\n\tMapedSql toMapedSql() throws Exception;\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/SqlLog.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport org.yx.db.visit.SumkStatement;\n\npublic interface SqlLog {\n\n\tvoid log(SumkStatement state, int totalTime, Throwable ex);\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/TableBootWatcher.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.annotation.Exclude;\nimport org.yx.bean.ParallelBootWatcher;\nimport org.yx.common.util.S;\nimport org.yx.db.spec.DBSpecs;\nimport org.yx.db.spec.TableSpec;\nimport org.yx.log.Logs;\n\npublic class TableBootWatcher extends ParallelBootWatcher {\n\n\tpublic TableBootWatcher() {\n\t\tDBSettings.init();\n\t}\n\n\t@Override\n\tprotected void handle(Class<?> clz) throws Exception {\n\t\tTableSpec spec = DBSpecs.extractTable(clz);\n\t\tif (spec == null) {\n\t\t\treturn;\n\t\t}\n\t\tList<ColumnMeta> columns = this.extractColumns(clz);\n\t\tPojoMetaHolder.register(clz, spec, columns);\n\t}\n\n\tpublic List<ColumnMeta> extractColumns(Class<?> pojoClz) {\n\t\tField[] fs = S.bean().getFields(pojoClz);\n\t\tMap<String, Field> map = new LinkedHashMap<>();\n\t\tfor (Field f : fs) {\n\t\t\tif (f.isAnnotationPresent(Exclude.class)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmap.putIfAbsent(f.getName(), f);\n\t\t}\n\t\tCollection<Field> set = map.values();\n\t\tList<ColumnMeta> list = new ArrayList<>(set.size());\n\t\tfor (Field f : set) {\n\t\t\tif (!f.isAccessible()) {\n\t\t\t\tf.setAccessible(true);\n\t\t\t}\n\t\t\tlist.add(new ColumnMeta(f, DBSpecs.extractColumn(pojoClz, f)));\n\t\t}\n\t\tif (list.isEmpty()) {\n\t\t\tLogs.db().debug(\"{}'s column is empty\", pojoClz.getName());\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tCollections.sort(list);\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn 2000;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/Update.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.yx.base.ItemJoiner;\nimport org.yx.base.sumk.map.ListMap;\nimport org.yx.common.util.kit.TypeConverter;\nimport org.yx.db.event.UpdateEvent;\nimport org.yx.db.visit.SumkDbVisitor;\nimport org.yx.exception.SumkException;\nimport org.yx.util.CollectionUtil;\n\npublic class Update extends ModifySqlBuilder {\n\n\tprivate Map<String, Object> updateTo;\n\n\tprivate Map<String, Number> incrMap;\n\n\t/**\n\t * @param update 如果为false，则数据库主键不会被更新。默认为false。\n\t * @return 当前对象\n\t */\n\tpublic Update updateDBID(boolean update) {\n\t\tthis.setOnOff(DBFlag.UPDATE_UPDATE_DBID, update);\n\t\treturn this;\n\t}\n\n\t/**\n\t * \n\t * @param onOff 如果为true，会验证map参数中，是否存在无效的key，预防开发人员将key写错。默认为true\n\t * @return 当前对象\n\t */\n\tpublic Update failIfPropertyNotMapped(boolean onOff) {\n\t\tthis.setOnOff(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED, onOff);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置where条件，如果没有设置该条件。就用pojo的数据库主键或者redis主键\n\t * <UL>\n\t * <LI>本方法可以被多次调用，多次调用之间是OR关系</LI>\n\t * <LI><B>注意：如果本表使用了缓存，本参数必须包含所有redis主键</B></LI>\n\t * <LI><B>注意：如果pojo是map类型，那么它的null值是有效条件</B></LI>\n\t * </UL>\n\t * \n\t * @param pojo bean类型或Map.如果是pojo对象，其中的null字段会被忽略掉\n\t * @return 当前对象\n\t */\n\n\tpublic Update addWhere(Object pojo) {\n\t\tthis._addIn(pojo);\n\t\treturn this;\n\t}\n\n\tpublic Update(SumkDbVisitor<Integer> visitor) {\n\t\tsuper(visitor);\n\t}\n\n\t/**\n\t * @param fullUpdate 设置为true的话，整条记录全部更新，包括null字段。默认为false\n\t * @return 当前对象\n\t */\n\tpublic Update fullUpdate(boolean fullUpdate) {\n\t\tthis.setOnOff(DBFlag.UPDATE_FULL_UPDATE, fullUpdate);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 分表的情况下，设置分区名。这个方法只能调用一次\n\t * \n\t * @param sub 分区名\n\t * @return 当前对象\n\t */\n\tpublic Update partition(String sub) {\n\t\tsub(sub);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 记录被更新后的最终状态。\n\t * <UL>\n\t * <LI>有可能是部分字段，有可能是全部字段</LI>\n\t * <LI>有可能只是单条记录变成这样，有可能是多条记录变成这样</LI>\n\t * </UL>\n\t * \n\t * @param pojo Pojo或Map类型.如果是Map类型，要设置tableClass。\n\t *             <B>如果本表使用了缓存，并且没有where条件，本参数必须包含所有redis主键</B>\n\t *             <B>如果本字段包含在自增长里面，那它将会被排除掉</B>\n\t * @return 当前对象\n\t */\n\tpublic Update updateTo(Object pojo) {\n\t\tthis.updateTo = this.populate(pojo, false);\n\t\treturn this;\n\t}\n\n\tpublic Update tableClass(Class<?> tableClass) {\n\t\tthis.tableClass = tableClass;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic MapedSql toMapedSql() throws Exception {\n\t\tif (this.updateTo == null) {\n\t\t\tthis.updateTo = Collections.emptyMap();\n\t\t}\n\t\tif (this.updateTo.isEmpty() && CollectionUtil.isEmpty(this.incrMap)) {\n\t\t\tthrow new SumkException(3464601, \"updateTo is null or empty\");\n\t\t}\n\n\t\tthis.checkMap(this.updateTo, this.pojoMeta);\n\t\tif (CollectionUtil.isEmpty(this.in)) {\n\t\t\tthis.addDBIDs2Where();\n\t\t}\n\t\treturn _toMapedSql();\n\t}\n\n\tprotected CharSequence buildSingleEqual(Map<String, Object> oneEqual, MapedSql ms) {\n\t\tif (oneEqual.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tItemJoiner andItem = new ItemJoiner(\" AND \", \" ( \", \" ) \");\n\t\tfor (Entry<String, Object> en : oneEqual.entrySet()) {\n\t\t\tColumnMeta fm = pojoMeta.getByFieldName(en.getKey());\n\t\t\tif (fm == null) {\n\t\t\t\tthis.failIfNotAllowPropertyMiss(en.getKey());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tObject value = en.getValue();\n\t\t\tif (value == null) {\n\t\t\t\tandItem.item().append(fm.dbColumn).append(\" IS NULL\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tandItem.item().append(fm.dbColumn).append(\" = ?\");\n\t\t\tms.addParam(value);\n\t\t}\n\t\tif (andItem.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tSoftDeleteMeta softDelete = pojoMeta.softDelete;\n\t\tif (softDelete != null) {\n\t\t\tif (softDelete.equalValid) {\n\t\t\t\tandItem.item().append(softDelete.columnName).append(\" = ?\");\n\t\t\t\tms.addParam(softDelete.validValue);\n\t\t\t} else {\n\t\t\t\tandItem.item().append(softDelete.columnName).append(\" <> ?\");\n\t\t\t\tms.addParam(softDelete.inValidValue);\n\t\t\t}\n\t\t}\n\t\treturn andItem.toCharSequence();\n\t}\n\n\tprotected Map<String, Object> buildSetUpdateField(StringBuilder sb, MapedSql mapedSql) {\n\t\tif (this.incrMap != null) {\n\t\t\tthis.checkMap(incrMap, pojoMeta);\n\t\t}\n\t\tboolean notFirst = false;\n\t\tMap<String, Object> to = new ListMap<>(this.updateTo);\n\t\tObject value;\n\t\tfor (ColumnMeta fm : pojoMeta.fieldMetas()) {\n\t\t\tfinal String fieldName = fm.getFieldName();\n\t\t\tif (this.incrMap != null && (value = this.incrMap.get(fieldName)) != null) {\n\t\t\t\tto.remove(fieldName);\n\t\t\t\tsb.append(notFirst ? \" , \" : \" SET \").append(fm.dbColumn).append(\" = \").append(fm.dbColumn)\n\t\t\t\t\t\t.append(\" + ?\");\n\t\t\t\tmapedSql.addParam(value);\n\t\t\t\tnotFirst = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvalue = this.updateTo.get(fieldName);\n\n\t\t\tif (value == null && !this.updateTo.containsKey(fieldName) && !this.isOn(DBFlag.UPDATE_FULL_UPDATE)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (fm.isDBID() && !this.isOn(DBFlag.UPDATE_UPDATE_DBID)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmapedSql.addParam(value);\n\t\t\tsb.append(notFirst ? \" , \" : \" SET \").append(fm.dbColumn).append(\" = ?\");\n\t\t\tnotFirst = true;\n\t\t}\n\t\treturn to;\n\t}\n\n\tprotected MapedSql _toMapedSql() throws Exception {\n\t\tMapedSql mapedSql = new MapedSql();\n\t\tStringBuilder sb = new StringBuilder(64).append(\"UPDATE \").append(pojoMeta.getTableName());\n\n\t\tMap<String, Object> to = this.buildSetUpdateField(sb, mapedSql);\n\t\tsb.append(\" WHERE \");\n\n\t\tItemJoiner orItem = new ItemJoiner(\" OR \", null, null);\n\t\tfor (Map<String, Object> oneEqual : this.in) {\n\t\t\tCharSequence one = this.buildSingleEqual(oneEqual, mapedSql);\n\t\t\tif (one != null && one.length() > 0) {\n\t\t\t\torItem.item().append(one);\n\t\t\t}\n\t\t}\n\t\tCharSequence whereStr = orItem.toCharSequence();\n\t\tif (whereStr == null || whereStr.length() == 0) {\n\t\t\tthrow new SumkException(7345445, \"where cannot be null\");\n\t\t}\n\t\tsb.append(whereStr);\n\t\tmapedSql.sql = sb.toString();\n\t\tmapedSql.event = new UpdateEvent(pojoMeta.getTableName(), flag, to, this.incrMap, this.in);\n\t\treturn mapedSql;\n\t}\n\n\tprotected void addDBIDs2Where() throws Exception {\n\t\tList<ColumnMeta> whereColumns = this.pojoMeta.getDatabaseIds();\n\t\tMap<String, Object> paramMap = new ListMap<>(whereColumns.size());\n\t\tfor (ColumnMeta fm : whereColumns) {\n\t\t\tString fieldName = fm.getFieldName();\n\t\t\tparamMap.put(fieldName, this.updateTo.get(fieldName));\n\t\t}\n\t\tthis._addInByMap(paramMap);\n\t}\n\n\t/**\n\t * 增加或减少表中数字类型字段的值\n\t * \n\t * @param fieldName 需要增长或减少的字段的名字,不能是redis主键\n\t * @param v         如果是减少，直接用负数就行了\n\t * @return 当前对象\n\t */\n\tpublic Update incrNum(String fieldName, Number v) {\n\t\tif (v == null) {\n\t\t\tthrow new SumkException(5349238, \"cannot incr \" + fieldName + \"(java) by null\");\n\t\t}\n\t\tPojoMeta pm = makeSurePojoMeta();\n\t\tColumnMeta columnMeta = pm.getByFieldName(fieldName);\n\t\tif (columnMeta == null) {\n\t\t\tthrow new SumkException(5912239, \"cannot found java field \" + fieldName + \" in \" + pm.pojoClz);\n\t\t}\n\t\tv = TypeConverter.toType(v, columnMeta.field.getType(), true);\n\t\tif (this.incrMap == null) {\n\t\t\tthis.incrMap = new ListMap<>();\n\t\t}\n\t\tthis.incrMap.put(fieldName, v);\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/VisitCounter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql;\n\n/**\n * 数据库及cache的访问计数器\n */\npublic interface VisitCounter {\n\n\tpublic int getInterval();\n\n\tlong getCacheKeyVisits();\n\n\tlong getCacheKeyHits();\n\n\tlong getModifyCount();\n\n\tlong getQueryCount();\n\n\tvoid incrCacheHit(int c);\n\n\tvoid incrModifyCount();\n\n\tvoid incrQueryCount();\n\n\tboolean willVisitCache(int c);\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/token/MapValueHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql.token;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Function;\n\npublic class MapValueHandler implements Function<String, Object> {\n\tprivate final Map<String, Object> map;\n\n\tpublic MapValueHandler(Map<String, Object> param) {\n\t\tthis.map = param == null ? Collections.emptyMap() : param;\n\t}\n\n\t@Override\n\tpublic Object apply(String key) {\n\t\treturn map.get(key);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/token/MapedSqlTokenParser.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql.token;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.yx.db.sql.MapedSql;\n\npublic class MapedSqlTokenParser {\n\n\tprivate final String openToken;\n\tprivate final String closeToken;\n\tprivate final TokenHandler handler;\n\n\tpublic MapedSqlTokenParser(String openToken, String closeToken, TokenHandler handler) {\n\t\tthis.openToken = openToken;\n\t\tthis.closeToken = closeToken;\n\t\tthis.handler = handler;\n\t}\n\n\tpublic MapedSql parse(String text) {\n\t\tStringBuilder builder = new StringBuilder();\n\t\tList<Object> paramters = new ArrayList<>();\n\t\tif (text != null && text.length() > 0) {\n\t\t\tchar[] src = text.toCharArray();\n\t\t\tint offset = 0;\n\t\t\tint start = text.indexOf(openToken, offset);\n\t\t\twhile (start > -1) {\n\t\t\t\tif (start > 0 && src[start - 1] == '\\\\') {\n\n\t\t\t\t\tbuilder.append(src, offset, start - offset - 1).append(openToken);\n\t\t\t\t\toffset = start + openToken.length();\n\t\t\t\t} else {\n\t\t\t\t\tint end = text.indexOf(closeToken, start);\n\t\t\t\t\tif (end == -1) {\n\t\t\t\t\t\tbuilder.append(src, offset, src.length - offset);\n\t\t\t\t\t\toffset = src.length;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbuilder.append(src, offset, start - offset);\n\t\t\t\t\t\toffset = start + openToken.length();\n\t\t\t\t\t\tString content = new String(src, offset, end - offset);\n\t\t\t\t\t\tString value = handler.handleToken(content, paramters);\n\t\t\t\t\t\tbuilder.append(value != null ? value : this.openToken + content + this.closeToken);\n\t\t\t\t\t\toffset = end + closeToken.length();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstart = text.indexOf(openToken, offset);\n\t\t\t}\n\t\t\tif (offset < src.length) {\n\t\t\t\tbuilder.append(src, offset, src.length - offset);\n\t\t\t}\n\t\t}\n\t\treturn new MapedSql(builder.toString(), paramters);\n\t}\n\n\tpublic static interface TokenHandler {\n\n\t\tString handleToken(String content, List<Object> paramters);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/token/ReplaceTokenHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql.token;\n\nimport java.util.Map;\nimport java.util.function.Function;\n\npublic final class ReplaceTokenHandler implements StringTokenParser.TokenHandler {\n\n\tprivate final Function<String, Object> valueHandler;\n\n\tpublic static ReplaceTokenHandler createByMap(Map<String, Object> map) {\n\t\treturn new ReplaceTokenHandler(new MapValueHandler(map));\n\t}\n\n\tpublic static ReplaceTokenHandler create(Function<String, Object> valueHandler) {\n\t\treturn new ReplaceTokenHandler(valueHandler);\n\t}\n\n\tpublic ReplaceTokenHandler(Function<String, Object> valueHandler) {\n\t\tthis.valueHandler = valueHandler;\n\t}\n\n\t@Override\n\tpublic String handleToken(String content) {\n\t\tObject v = valueHandler.apply(content);\n\t\tif (v == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn v.toString();\n\t}\n\n}"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/token/StringTokenParser.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql.token;\n\npublic class StringTokenParser {\n\n\tprivate final String openToken;\n\tprivate final String closeToken;\n\tprivate final TokenHandler handler;\n\n\tpublic StringTokenParser(String openToken, String closeToken, TokenHandler handler) {\n\t\tthis.openToken = openToken;\n\t\tthis.closeToken = closeToken;\n\t\tthis.handler = handler;\n\t}\n\n\tpublic String parse(String text) {\n\t\tStringBuilder builder = new StringBuilder();\n\t\tif (text != null && text.length() > 0) {\n\t\t\tchar[] src = text.toCharArray();\n\t\t\tint offset = 0;\n\t\t\tint start = text.indexOf(openToken, offset);\n\t\t\twhile (start > -1) {\n\t\t\t\tif (start > 0 && src[start - 1] == '\\\\') {\n\n\t\t\t\t\tbuilder.append(src, offset, start - offset - 1).append(openToken);\n\t\t\t\t\toffset = start + openToken.length();\n\t\t\t\t} else {\n\t\t\t\t\tint end = text.indexOf(closeToken, start);\n\t\t\t\t\tif (end == -1) {\n\t\t\t\t\t\tbuilder.append(src, offset, src.length - offset);\n\t\t\t\t\t\toffset = src.length;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbuilder.append(src, offset, start - offset);\n\t\t\t\t\t\toffset = start + openToken.length();\n\t\t\t\t\t\tString content = new String(src, offset, end - offset);\n\t\t\t\t\t\tString value = handler.handleToken(content);\n\t\t\t\t\t\tbuilder.append(value != null ? value : this.openToken + content + this.closeToken);\n\t\t\t\t\t\toffset = end + closeToken.length();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstart = text.indexOf(openToken, offset);\n\t\t\t}\n\t\t\tif (offset < src.length) {\n\t\t\t\tbuilder.append(src, offset, src.length - offset);\n\t\t\t}\n\t\t}\n\t\treturn builder.toString();\n\t}\n\n\tpublic static interface TokenHandler {\n\n\t\tString handleToken(String content);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sql/token/VariableTokenHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sql.token;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\n\npublic class VariableTokenHandler implements MapedSqlTokenParser.TokenHandler {\n\n\tprivate final Function<String, Object> valueHandler;\n\n\tpublic static VariableTokenHandler createByMap(Map<String, Object> map) {\n\t\treturn new VariableTokenHandler(new MapValueHandler(map));\n\t}\n\n\tpublic static VariableTokenHandler create(Function<String, Object> valueHandler) {\n\t\treturn new VariableTokenHandler(valueHandler);\n\t}\n\n\tpublic VariableTokenHandler(Function<String, Object> valueHandler) {\n\t\tthis.valueHandler = valueHandler;\n\t}\n\n\t@Override\n\tpublic String handleToken(String content, List<Object> paramters) {\n\t\tparamters.add(valueHandler.apply(content));\n\t\treturn \"?\";\n\t}\n}"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sumk/batis/ConfigurationFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sumk.batis;\n\nimport org.apache.ibatis.session.Configuration;\nimport org.yx.base.Ordered;\n\npublic interface ConfigurationFactory extends Ordered {\n\n\tConfiguration create(String name);\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sumk/batis/ProxySession.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sumk.batis;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.ibatis.cursor.Cursor;\nimport org.apache.ibatis.executor.BatchResult;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\nimport org.apache.ibatis.session.SqlSession;\nimport org.yx.db.conn.ConnectionPool;\nimport org.yx.db.enums.DBType;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\n\npublic class ProxySession implements SqlSession {\n\n\t@Override\n\tpublic Connection getConnection() {\n\t\tthrow new SumkException(148675, \"getConnection not support\");\n\t}\n\n\tprotected SqlSession readSession() {\n\t\tConnectionPool context = ConnectionPool.get();\n\t\ttry {\n\t\t\treturn SqlSessionFactory.get(context.getDbName()).openSession(context.connection(DBType.READ_PREFER));\n\t\t} catch (SQLException e) {\n\t\t\tLog.get(\"sumk.mybatis\").error(e.getMessage(), e);\n\t\t\tthrow new SumkException(345254, \"创建mybatis的读session失败\");\n\t\t}\n\t}\n\n\tprotected SqlSession writeSession() {\n\t\tConnectionPool context = ConnectionPool.get();\n\t\ttry {\n\t\t\treturn SqlSessionFactory.get(context.getDbName()).openSession(context.connection(DBType.WRITE));\n\t\t} catch (SQLException e) {\n\t\t\tLog.get(\"sumk.mybatis\").error(e.getMessage(), e);\n\t\t\tthrow new SumkException(345255, \"创建mybatis的写session失败\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic <T> T selectOne(String statement) {\n\t\treturn this.readSession().selectOne(statement);\n\t}\n\n\t@Override\n\tpublic <T> T selectOne(String statement, Object parameter) {\n\t\treturn this.readSession().selectOne(statement, parameter);\n\t}\n\n\t@Override\n\tpublic <K, V> Map<K, V> selectMap(String statement, String mapKey) {\n\t\treturn this.readSession().selectMap(statement, mapKey);\n\t}\n\n\t@Override\n\tpublic <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {\n\t\treturn this.readSession().selectMap(statement, parameter, mapKey);\n\t}\n\n\t@Override\n\tpublic <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {\n\t\treturn this.readSession().selectMap(statement, parameter, mapKey, rowBounds);\n\t}\n\n\t@Override\n\tpublic <E> List<E> selectList(String statement) {\n\t\treturn this.readSession().selectList(statement);\n\t}\n\n\t@Override\n\tpublic <E> List<E> selectList(String statement, Object parameter) {\n\t\treturn this.readSession().selectList(statement, parameter);\n\t}\n\n\t@Override\n\tpublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {\n\t\treturn this.readSession().selectList(statement, parameter, rowBounds);\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\t@Override\n\tpublic void select(String statement, Object parameter, ResultHandler handler) {\n\t\tthis.readSession().select(statement, parameter, handler);\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\t@Override\n\tpublic void select(String statement, ResultHandler handler) {\n\t\tthis.readSession().select(statement, handler);\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\t@Override\n\tpublic void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {\n\t\tthis.readSession().select(statement, parameter, rowBounds, handler);\n\t}\n\n\t@Override\n\tpublic int insert(String statement) {\n\t\treturn this.writeSession().insert(statement);\n\t}\n\n\t@Override\n\tpublic int insert(String statement, Object parameter) {\n\t\treturn this.writeSession().insert(statement, parameter);\n\t}\n\n\t@Override\n\tpublic int update(String statement) {\n\t\treturn this.writeSession().update(statement);\n\t}\n\n\t@Override\n\tpublic int update(String statement, Object parameter) {\n\t\treturn this.writeSession().update(statement, parameter);\n\t}\n\n\t@Override\n\tpublic int delete(String statement) {\n\t\treturn this.writeSession().delete(statement);\n\t}\n\n\t@Override\n\tpublic int delete(String statement, Object parameter) {\n\t\treturn this.writeSession().delete(statement, parameter);\n\t}\n\n\t@Override\n\tpublic void commit() {\n\t}\n\n\t@Override\n\tpublic void commit(boolean force) {\n\t}\n\n\t@Override\n\tpublic void rollback() {\n\t}\n\n\t@Override\n\tpublic void rollback(boolean force) {\n\t}\n\n\t@Override\n\tpublic List<BatchResult> flushStatements() {\n\t\treturn this.writeSession().flushStatements();\n\t}\n\n\t@Override\n\tpublic void close() {\n\t}\n\n\t@Override\n\tpublic Configuration getConfiguration() {\n\t\treturn this.writeSession().getConfiguration();\n\t}\n\n\t@Override\n\tpublic <T> T getMapper(Class<T> type) {\n\t\treturn this.writeSession().getMapper(type);\n\t}\n\n\t@Override\n\tpublic void clearCache() {\n\t\tthis.writeSession().clearCache();\n\t}\n\n\t@Override\n\tpublic <T> Cursor<T> selectCursor(String statement) {\n\t\treturn this.readSession().selectCursor(statement);\n\t}\n\n\t@Override\n\tpublic <T> Cursor<T> selectCursor(String statement, Object parameter) {\n\t\treturn this.readSession().selectCursor(statement, parameter);\n\t}\n\n\t@Override\n\tpublic <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {\n\t\treturn this.readSession().selectCursor(statement, parameter, rowBounds);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sumk/batis/SqlSessionFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sumk.batis;\n\nimport java.io.ByteArrayInputStream;\nimport java.sql.Connection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.function.Supplier;\n\nimport org.apache.ibatis.builder.xml.XMLMapperBuilder;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.defaults.DefaultSqlSession;\nimport org.apache.ibatis.transaction.Transaction;\nimport org.apache.ibatis.transaction.managed.ManagedTransaction;\nimport org.yx.bean.IOC;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.LocalMultiResourceLoaderSupplier;\nimport org.yx.conf.MultiResourceLoader;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.util.StringUtil;\n\npublic class SqlSessionFactory {\n\n\tprivate static final ConcurrentMap<String, SqlSessionFactory> factoryMap = new ConcurrentHashMap<>();\n\n\tprivate Configuration configuration;\n\tprivate String db;\n\n\tprivate SqlSessionFactory() {\n\n\t}\n\n\tprivate static SqlSessionFactory create(String dbName) throws Exception {\n\t\tSqlSessionFactory sessionFactory = new SqlSessionFactory();\n\t\tsessionFactory.db = dbName;\n\t\tList<ConfigurationFactory> confFactorys = IOC.getBeans(ConfigurationFactory.class);\n\t\tif (confFactorys != null && confFactorys.size() > 0) {\n\t\t\tfor (ConfigurationFactory f : confFactorys) {\n\t\t\t\tConfiguration conf = f.create(dbName);\n\t\t\t\tif (conf != null) {\n\t\t\t\t\tsessionFactory.configuration = conf;\n\t\t\t\t\treturn sessionFactory.sqlParse();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tConfiguration conf = new Configuration();\n\t\tconf.setDefaultExecutorType(ExecutorType.SIMPLE);\n\t\tconf.setCacheEnabled(false);\n\t\tsessionFactory.configuration = conf;\n\t\treturn sessionFactory.sqlParse();\n\t}\n\n\tprivate static Supplier<MultiResourceLoader> resourceLoader = new LocalMultiResourceLoaderSupplier(\n\t\t\tAppInfo.get(\"sumk.db.mybatis.path\", AppInfo.CLASSPATH_URL_PREFIX + \"batis\"));\n\n\tpublic static void setResourceLoader(Supplier<MultiResourceLoader> resourceLoader) {\n\t\tSqlSessionFactory.resourceLoader = Objects.requireNonNull(resourceLoader);\n\t}\n\n\tpublic static Supplier<MultiResourceLoader> getResourceLoader() {\n\t\treturn resourceLoader;\n\t}\n\n\tpublic static SqlSessionFactory get(String dbName) {\n\t\tSqlSessionFactory factory = factoryMap.get(dbName);\n\t\tif (factory != null) {\n\t\t\treturn factory;\n\t\t}\n\t\ttry {\n\t\t\tfactory = factoryMap.computeIfAbsent(dbName, name -> {\n\t\t\t\tLogs.db().info(\"mybatis创建{}的SqlSessionFactory\", name);\n\t\t\t\ttry {\n\t\t\t\t\treturn SqlSessionFactory.create(name);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLogs.db().error(\"创建\" + name + \"的SqlSessionFactory失败\", e);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (factory != null) {\n\t\t\t\treturn factory;\n\t\t\t}\n\t\t\treturn factoryMap.get(dbName);\n\t\t} catch (Exception e) {\n\t\t\tLog.printStack(\"sumk.sql.error\", e);\n\t\t\tthrow new SumkException(100234325, dbName + \" create SqlSessionFactory failed\");\n\t\t}\n\t}\n\n\tvoid destroy() {\n\t}\n\n\tpublic static void reload(String dbName) throws Exception {\n\t\tdbName = StringUtil.requireNotEmpty(dbName).trim();\n\t\tSqlSessionFactory factory = factoryMap.get(dbName);\n\t\tif (factory == null) {\n\t\t\treturn;\n\t\t}\n\t\tfactory = SqlSessionFactory.create(dbName);\n\t\tSqlSessionFactory old = factoryMap.put(dbName, factory);\n\t\told.destroy();\n\t}\n\n\tpublic SqlSession openSession(Connection conn) {\n\n\t\tTransaction transaction = new ManagedTransaction(conn, false);\n\t\tExecutor executor = configuration.newExecutor(transaction);\n\t\treturn new DefaultSqlSession(configuration, executor);\n\t}\n\n\tSqlSessionFactory sqlParse() throws Exception {\n\t\tMap<String, byte[]> sqls = resourceLoader.get().openResources(db);\n\t\tSet<Entry<String, byte[]>> entries = sqls.entrySet();\n\t\tfor (Entry<String, byte[]> entry : entries) {\n\t\t\tbyte[] bs = entry.getValue();\n\t\t\tXMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(new ByteArrayInputStream(bs), configuration,\n\t\t\t\t\tentry.getKey(), configuration.getSqlFragments());\n\t\t\txmlMapperBuilder.parse();\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic Configuration getConfiguration() {\n\t\treturn this.configuration;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/sumk/batis/SqlSessionHolder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.sumk.batis;\n\nimport org.apache.ibatis.session.SqlSession;\n\n/**\n * 不要作为全局变量，要作为方法的局部变量，要在@Box里面调用\n * 因为数据库连接池重写了connection.close()，所以可以尽情的调用session.close()<BR>\n * 供外部调用的类\n */\npublic abstract class SqlSessionHolder {\n\n\tpublic static SqlSession session() {\n\t\treturn new ProxySession();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/Exchange.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.visit;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.log.Log;\nimport org.yx.redis.RedisPool;\nimport org.yx.util.StringUtil;\n\npublic class Exchange {\n\n\tprivate List<Map<String, Object>> leftIn = Collections.emptyList();\n\n\tprivate List<String> data;\n\n\tpublic Exchange() {\n\t}\n\n\tpublic void setLeftIn(List<Map<String, Object>> leftIn) {\n\t\tthis.leftIn = Objects.requireNonNull(leftIn);\n\t}\n\n\tpublic List<Map<String, Object>> getLeftIn() {\n\t\treturn this.leftIn;\n\t}\n\n\tpublic List<String> getData() {\n\t\treturn data;\n\t}\n\n\tpublic void findFromCache(PojoMeta pm) {\n\t\tList<Map<String, Object>> origin = this.leftIn;\n\n\t\tif (origin == null || origin.isEmpty() || RedisPool.defaultRedis() == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tList<String> redisList = new ArrayList<>(origin.size());\n\t\t\tList<Map<String, Object>> redisConditions = new ArrayList<>(origin.size());\n\n\t\t\tList<Map<String, Object>> notFound = new ArrayList<>(origin.size());\n\t\t\tfor (Map<String, Object> map : origin) {\n\t\t\t\tif (pm.isOnlyCacheID(map)) {\n\t\t\t\t\tredisList.add(pm.getCacheID(map, false));\n\t\t\t\t\tredisConditions.add(map);\n\t\t\t\t} else {\n\t\t\t\t\tnotFound.add(map);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (redisList.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tList<String> redisData = RecordRepository.getMultiValue(pm, redisList);\n\t\t\tif (redisData == null || redisData.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.data = new ArrayList<>(redisData.size());\n\t\t\tfor (int i = 0; i < redisConditions.size(); i++) {\n\t\t\t\tMap<String, Object> conditon = redisConditions.get(i);\n\n\t\t\t\tif (i < redisData.size() && StringUtil.isNotEmpty(redisData.get(i))) {\n\t\t\t\t\tthis.data.add(redisData.get(i));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tnotFound.add(conditon);\n\t\t\t}\n\t\t\tthis.leftIn = notFound.isEmpty() ? null : notFound;\n\t\t} catch (Exception e) {\n\t\t\tthis.data = null;\n\t\t\tLog.printStack(\"sumk.sql\", e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/MapResultHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.visit;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.yx.db.DBJson;\nimport org.yx.db.enums.CacheType;\nimport org.yx.db.sql.ColumnMeta;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic class MapResultHandler implements ResultHandler {\n\n\tpublic static final MapResultHandler handler = new MapResultHandler();\n\n\tpublic static Map<String, Object> filterSelectColumns(Map<String, Object> map, List<String> selectColumns) {\n\t\tif (selectColumns.isEmpty()) {\n\t\t\treturn map;\n\t\t}\n\t\tIterator<String> it = map.keySet().iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tString key = it.next();\n\t\t\tif (!selectColumns.contains(key)) {\n\t\t\t\tit.remove();\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic <T> List<T> parseFromJson(PojoMeta pm, List<String> jsons, List<String> selectColumns)\n\t\t\tthrows InstantiationException, IllegalAccessException {\n\t\tif (CollectionUtil.isEmpty(jsons)) {\n\t\t\treturn null;\n\t\t}\n\t\tList<Map<String, Object>> list = new ArrayList<>();\n\t\tfor (String json : jsons) {\n\t\t\tif (StringUtil.isEmpty(json)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (pm.cacheType() == CacheType.LIST || (json.startsWith(\"[\") && json.endsWith(\"]\"))) {\n\t\t\t\tObject[] ts = DBJson.operator().fromJson(json, pm.pojoArrayClz());\n\t\t\t\tif (ts == null || ts.length == 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfor (Object obj : ts) {\n\t\t\t\t\tMap<String, Object> map = pm.populate(obj, false);\n\t\t\t\t\tmap = filterSelectColumns(map, selectColumns);\n\t\t\t\t\tif (map.size() > 0) {\n\t\t\t\t\t\tlist.add(map);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tObject obj = DBJson.operator().fromJson(json, pm.pojoClz());\n\t\t\tif (obj == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tMap<String, Object> map = pm.populate(obj, false);\n\t\t\tmap = filterSelectColumns(map, selectColumns);\n\t\t\tif (map.size() > 0) {\n\t\t\t\tlist.add(map);\n\t\t\t}\n\t\t}\n\t\treturn (List<T>) list;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic <T> List<T> parse(PojoMeta pm, List<Map<ColumnMeta, Object>> list) {\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<Map<String, Object>> ret = new ArrayList<>(list.size());\n\t\tfor (Map<ColumnMeta, Object> m0 : list) {\n\t\t\tMap<String, Object> map = new HashMap<>();\n\t\t\tfor (Entry<ColumnMeta, Object> en : m0.entrySet()) {\n\t\t\t\tmap.put(en.getKey().getFieldName(), en.getValue());\n\t\t\t}\n\t\t\tret.add(map);\n\t\t}\n\t\treturn (List<T>) ret;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/PojoResultHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.visit;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.yx.db.DBJson;\nimport org.yx.db.enums.CacheType;\nimport org.yx.db.sql.ColumnMeta;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\npublic class PojoResultHandler implements ResultHandler {\n\n\tpublic static final PojoResultHandler handler = new PojoResultHandler();\n\n\tpublic void filterSelectColumns(PojoMeta pm, Object obj, List<String> selectColumns) throws Exception {\n\t\tif (selectColumns.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (ColumnMeta cm : pm.fieldMetas()) {\n\t\t\tif (!selectColumns.contains(cm.getFieldName())) {\n\t\t\t\tcm.getField().set(obj, null);\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic <T> List<T> parseFromJson(PojoMeta pm, List<String> jsons, List<String> selectColumns) throws Exception {\n\t\tif (CollectionUtil.isEmpty(jsons)) {\n\t\t\treturn null;\n\t\t}\n\t\tList<Object> list = new ArrayList<>(jsons.size());\n\t\tfor (String json : jsons) {\n\t\t\tif (StringUtil.isEmpty(json)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (pm.cacheType() == CacheType.LIST || (json.startsWith(\"[\") && json.endsWith(\"]\"))) {\n\t\t\t\tObject[] ts = DBJson.operator().fromJson(json, pm.pojoArrayClz());\n\t\t\t\tif (ts == null || ts.length == 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfor (Object t : ts) {\n\t\t\t\t\tthis.filterSelectColumns(pm, t, selectColumns);\n\t\t\t\t\tlist.add(t);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tObject obj = DBJson.operator().fromJson(json, pm.pojoClz());\n\t\t\tif (obj == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.filterSelectColumns(pm, obj, selectColumns);\n\t\t\tlist.add(obj);\n\t\t}\n\t\treturn (List<T>) list;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic <T> List<T> parse(PojoMeta pm, List<Map<ColumnMeta, Object>> list) throws Exception {\n\t\tList<Object> ret = new ArrayList<>();\n\t\tfor (Map<ColumnMeta, Object> map : list) {\n\t\t\tif (map.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tret.add(buildPojo(pm, map));\n\t\t}\n\t\treturn (List<T>) ret;\n\t}\n\n\tprivate Object buildPojo(PojoMeta pm, Map<ColumnMeta, Object> map) throws Exception {\n\t\tObject obj = Loader.newInstance(pm.pojoClz());\n\t\tfor (Entry<ColumnMeta, Object> en : map.entrySet()) {\n\t\t\tif (en.getValue() == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ten.getKey().setValue(obj, en.getValue());\n\t\t}\n\t\treturn obj;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/RecordAccess.java",
    "content": "package org.yx.db.visit;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.yx.db.sql.PojoMeta;\n\n/**\n * 本接口只负责实际的存取，实现类一般不需要null判断等\n */\npublic interface RecordAccess {\n\n\tString get(PojoMeta m, String id);\n\n\tList<String> getMultiValue(PojoMeta m, Collection<String> ids);\n\n\tvoid set(PojoMeta m, String id, String json);\n\n\tvoid del(PojoMeta m, String id);\n\n\tvoid delMulti(PojoMeta m, String[] ids);\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/RecordRepository.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.visit;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.exception.SumkException;\n\npublic final class RecordRepository {\n\tprivate static RecordAccess access = new RedisAccess();\n\n\tpublic static RecordAccess access() {\n\t\treturn access;\n\t}\n\n\tpublic static void setAccess(RecordAccess access) {\n\t\tRecordRepository.access = Objects.requireNonNull(access);\n\t}\n\n\tpublic static String get(PojoMeta m, String id) {\n\t\tif (!m.getCounter().willVisitCache(1)) {\n\t\t\treturn null;\n\t\t}\n\t\tString s = access.get(m, id);\n\t\tif (s != null) {\n\t\t\tm.getCounter().incrCacheHit(1);\n\t\t}\n\t\treturn s;\n\t}\n\n\tpublic static void set(PojoMeta m, String id, String json) {\n\t\tif (json == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (id == null || id.isEmpty()) {\n\t\t\tthrow new SumkException(657645465, \"key of redis value cannot be null\");\n\t\t}\n\t\taccess.set(m, id, json);\n\t}\n\n\tpublic static void del(PojoMeta m, String id) {\n\t\taccess.del(m, id);\n\t}\n\n\tpublic static void delMulti(PojoMeta m, String[] ids) {\n\t\tif (ids == null || ids.length == 0) {\n\t\t\treturn;\n\t\t}\n\t\taccess.delMulti(m, ids);\n\t}\n\n\tpublic static List<String> getMultiValue(PojoMeta m, Collection<String> ids) {\n\t\tif (ids == null || ids.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\tif (!m.getCounter().willVisitCache(ids.size())) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\tList<String> ret = access.getMultiValue(m, ids);\n\t\tif (ret == null || ret.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (ret != null && ret.size() > 0) {\n\t\t\tint c = 0;\n\t\t\tfor (String v : ret) {\n\t\t\t\tif (v != null) {\n\t\t\t\t\tc++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tm.getCounter().incrCacheHit(c);\n\t\t}\n\t\treturn ret;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/RedisAccess.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.visit;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.log.Log;\nimport org.yx.redis.Redis;\nimport org.yx.redis.RedisPool;\n\npublic final class RedisAccess implements RecordAccess {\n\n\tprivate static Logger logger = Log.get(\"sumk.redis.access\");\n\n\tprotected Redis muteRedis(String tableName) {\n\t\treturn RedisPool.get(tableName).mute();\n\t}\n\n\tprotected String getKey(PojoMeta m, String id) {\n\t\treturn m.getPre() + id;\n\t}\n\n\tprotected String[] getKeys(PojoMeta m, String[] ids) {\n\t\tString[] keys = new String[ids.length];\n\t\tfor (int i = 0; i < ids.length; i++) {\n\t\t\tkeys[i] = getKey(m, ids[i]);\n\t\t}\n\t\treturn keys;\n\t}\n\n\t@Override\n\tpublic String get(PojoMeta m, String id) {\n\t\treturn muteRedis(m.getTableName()).get(getKey(m, id));\n\t}\n\n\t@Override\n\tpublic void set(PojoMeta m, String id, String json) {\n\t\tString key = getKey(m, id);\n\t\tString tableName = m.getTableName();\n\t\tint ttl = m.getTtlSec();\n\t\tif (ttl <= 0) {\n\t\t\tmuteRedis(tableName).set(key, json);\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(\"{} >> SET {} = {}\", tableName, key, json);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tmuteRedis(tableName).setex(key, ttl, json);\n\t\tif (logger.isTraceEnabled()) {\n\t\t\tlogger.trace(\"{} >> SETEX {} = {}\", tableName, key, json);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void del(PojoMeta m, String id) {\n\t\tString key = getKey(m, id);\n\t\tString tableName = m.getTableName();\n\t\tmuteRedis(tableName).del(key);\n\t\tif (logger.isTraceEnabled()) {\n\t\t\tlogger.trace(\"{} >> DELETE {}\", tableName, key);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void delMulti(PojoMeta m, String[] ids) {\n\t\tString[] keys = getKeys(m, ids);\n\t\tmuteRedis(m.getTableName()).del(keys);\n\t\tif (logger.isTraceEnabled()) {\n\t\t\tString ks = Arrays.toString(keys);\n\t\t\tks = ks.substring(1, ks.length() - 1);\n\t\t\tlogger.trace(\"{} >> DEL_MULTI {}\", m.getTableName(), ks);\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<String> getMultiValue(PojoMeta m, Collection<String> ids) {\n\t\tString[] keys = getKeys(m, ids.toArray(new String[ids.size()]));\n\t\treturn muteRedis(m.getTableName()).mget(keys);\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/ResultHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.visit;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.db.sql.ColumnMeta;\nimport org.yx.db.sql.PojoMeta;\n\n/**\n * 处理结果要包含所有的原始字段，并且返回值的toJson()也不能有字段损失\n */\npublic interface ResultHandler {\n\n\t<T> List<T> parseFromJson(PojoMeta pm, List<String> jsons, List<String> selectColumns) throws Exception;\n\n\t<T> List<T> parse(PojoMeta pm, List<Map<ColumnMeta, Object>> list) throws Exception;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/ResultSetUtils.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.visit;\n\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.base.sumk.map.ListMap;\nimport org.yx.common.util.kit.Asserts;\nimport org.yx.db.sql.ColumnMeta;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.exception.SumkException;\n\npublic final class ResultSetUtils {\n\tpublic static List<Map<String, Object>> toMapList(ResultSet rs) throws java.sql.SQLException {\n\t\tList<Map<String, Object>> list = new ArrayList<>(10);\n\t\tif (rs == null) {\n\t\t\treturn list;\n\t\t}\n\t\tResultSetMetaData md = rs.getMetaData();\n\t\tint columnCount = md.getColumnCount();\n\t\tMap<String, Object> rowData;\n\t\twhile (rs.next()) {\n\t\t\trowData = new HashMap<>();\n\t\t\tfor (int i = 1; i <= columnCount; i++) {\n\t\t\t\trowData.put(md.getColumnName(i), rs.getObject(i));\n\t\t\t}\n\t\t\tlist.add(rowData);\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic static List<Map<ColumnMeta, Object>> toMapList(ResultSet rs, PojoMeta pm) throws java.sql.SQLException {\n\t\tList<Map<ColumnMeta, Object>> list = new ArrayList<>();\n\t\tif (rs == null) {\n\t\t\treturn list;\n\t\t}\n\t\tResultSetMetaData md = rs.getMetaData();\n\t\tint columnCount = md.getColumnCount();\n\t\tColumnMeta[] columns = new ColumnMeta[columnCount + 1];\n\t\tfor (int i = 1; i <= columnCount; i++) {\n\t\t\tColumnMeta cm = pm.getByColumnDBName(md.getColumnName(i));\n\t\t\tif (cm == null) {\n\t\t\t\tthrow new SumkException(629234, md.getColumnName(i) + \"这个字段没有在java的pojo类中定义\");\n\t\t\t}\n\t\t\tcolumns[i] = cm;\n\t\t}\n\t\tMap<ColumnMeta, Object> rowData;\n\t\twhile (rs.next()) {\n\t\t\trowData = new ListMap<>(columnCount);\n\t\t\tfor (int i = 1; i <= columnCount; i++) {\n\t\t\t\trowData.put(columns[i], rs.getObject(i));\n\t\t\t}\n\t\t\tlist.add(rowData);\n\t\t}\n\t\trs.close();\n\t\treturn list;\n\t}\n\n\tpublic static List<Object> toList(ResultSet rs) throws java.sql.SQLException {\n\t\tList<Object> list = new ArrayList<>(10);\n\t\tif (rs == null) {\n\t\t\treturn list;\n\t\t}\n\t\tResultSetMetaData md = rs.getMetaData();\n\t\tAsserts.requireTrue(md.getColumnCount() == 1, \"result data column is \" + md.getColumnCount() + \", not 1\");\n\t\twhile (rs.next()) {\n\t\t\tlist.add(rs.getObject(1));\n\t\t}\n\t\trs.close();\n\t\treturn list;\n\t}\n\n\tpublic static List<Object[]> toObjectArrayList(ResultSet rs) throws java.sql.SQLException {\n\t\tList<Object[]> list = new ArrayList<>(10);\n\t\tif (rs == null) {\n\t\t\treturn list;\n\t\t}\n\t\tResultSetMetaData md = rs.getMetaData();\n\t\tfinal int len = md.getColumnCount();\n\t\tObject[] data;\n\t\twhile (rs.next()) {\n\t\t\tdata = new Object[len];\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tdata[i] = rs.getObject(i + 1);\n\t\t\t}\n\t\t\tlist.add(data);\n\t\t}\n\t\trs.close();\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/SumkDbVisitor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.visit;\n\nimport org.yx.db.sql.SqlBuilder;\n\npublic interface SumkDbVisitor<T> {\n\n\tT visit(SqlBuilder builder) throws Exception;\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/SumkStatement.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.visit;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.sql.Timestamp;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.BiConsumer;\n\nimport org.slf4j.Logger;\nimport org.yx.db.kit.DBKits;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.db.sql.MapedSql;\nimport org.yx.db.sql.SqlLog;\nimport org.yx.exception.SumkException;\nimport org.yx.exception.SumkExceptionCode;\nimport org.yx.log.CodeLineMarker;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.util.SumkDate;\n\npublic class SumkStatement implements AutoCloseable {\n\tprivate static final Logger LOG = Log.get(\"sumk.sql.plain\");\n\tprivate static long executeCount;\n\tprivate static SqlLog sqlLog = (a, b, c) -> {\n\t};\n\tprivate static CodeLineMarker marker = new CodeLineMarker(\"org.yx.db.\");\n\tprivate static BiConsumer<PreparedStatement, List<Object>> statementParamAttacher = (ps, params) -> {\n\t\tint size = params.size();\n\t\tif (size == 0) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\tObject parameterObj = params.get(i);\n\t\t\t\tif (parameterObj == null) {\n\t\t\t\t\tps.setNull(i + 1, java.sql.Types.OTHER);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (parameterObj.getClass() == java.util.Date.class) {\n\t\t\t\t\tps.setTimestamp(i + 1, new Timestamp(((java.util.Date) parameterObj).getTime()));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (parameterObj.getClass() == SumkDate.class) {\n\t\t\t\t\tps.setTimestamp(i + 1, ((SumkDate) parameterObj).toTimestamp());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tps.setObject(i + 1, parameterObj);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(3643654,\n\t\t\t\t\t\"设置PreparedStatement的参数失败，sql语句: \" + DBKits.getSqlOfStatement(ps) + \" ,参数: \" + params, e);\n\t\t}\n\t};\n\n\tpublic static BiConsumer<PreparedStatement, List<Object>> getStatementParamAttacher() {\n\t\treturn statementParamAttacher;\n\t}\n\n\tpublic static void setStatementParamAttacher(BiConsumer<PreparedStatement, List<Object>> statementParamAttacher) {\n\t\tSumkStatement.statementParamAttacher = Objects.requireNonNull(statementParamAttacher);\n\t}\n\n\tprivate final AtomicReference<PreparedStatement> statement;\n\tprivate final MapedSql maped;\n\tprivate final long beginTime;\n\tprivate int sqlTime;\n\tprivate int modifyCount = -1;\n\tprivate Throwable exception;\n\n\t/**\n\t * 非空\n\t * \n\t * @return marker实例\n\t */\n\tpublic static CodeLineMarker getMarker() {\n\t\treturn marker;\n\t}\n\n\tpublic static void setMarker(CodeLineMarker marker) {\n\t\tSumkStatement.marker = Objects.requireNonNull(marker);\n\t}\n\n\tpublic static void setSqlLog(SqlLog sqlLog) {\n\t\tSumkStatement.sqlLog = Objects.requireNonNull(sqlLog);\n\t}\n\n\tstatic SumkStatement create(Connection conn, MapedSql maped) throws Exception {\n\t\treturn new SumkStatement(conn.prepareStatement(maped.getSql()), maped);\n\t}\n\n\tstatic SumkStatement createAutoGenerateKeyStatement(Connection conn, MapedSql maped) throws Exception {\n\t\treturn new SumkStatement(conn.prepareStatement(maped.getSql(), Statement.RETURN_GENERATED_KEYS), maped);\n\t}\n\n\tprivate SumkStatement(PreparedStatement statement, MapedSql maped) {\n\t\tthis.statement = new AtomicReference<>(statement);\n\t\tthis.maped = maped;\n\t\tbeginTime = System.currentTimeMillis();\n\t\tattachParams();\n\t}\n\n\tprivate PreparedStatement checkStatement() throws SQLException {\n\t\tPreparedStatement statement = this.statement.get();\n\t\tif (statement == null) {\n\t\t\tthrow new SumkException(SumkExceptionCode.DB_CONNECTION_CLOSED, \"连接已关闭\");\n\t\t}\n\t\texecuteCount++;\n\t\treturn statement;\n\t}\n\n\tpublic int executeUpdate() throws SQLException {\n\t\tPreparedStatement statement = checkStatement();\n\t\tmodifyCount = 0;\n\t\ttry {\n\t\t\tmodifyCount = statement.executeUpdate();\n\t\t} catch (Exception e) {\n\t\t\tthis.exception = e;\n\t\t\tsqlTime = (int) (System.currentTimeMillis() - beginTime);\n\t\t\tthrow e;\n\t\t}\n\t\tsqlTime = (int) (System.currentTimeMillis() - beginTime);\n\t\treturn modifyCount;\n\t}\n\n\tpublic ResultSet executeQuery() throws Exception {\n\t\tPreparedStatement statement = checkStatement();\n\t\tResultSet ret = null;\n\t\ttry {\n\t\t\tret = statement.executeQuery();\n\t\t} catch (Exception e) {\n\t\t\tthis.exception = e;\n\t\t\tsqlTime = (int) (System.currentTimeMillis() - beginTime);\n\t\t\tthrow e;\n\t\t}\n\t\tsqlTime = (int) (System.currentTimeMillis() - beginTime);\n\t\treturn ret;\n\t}\n\n\t@Override\n\tpublic void close() throws SQLException {\n\t\tPreparedStatement statement = this.statement.getAndSet(null);\n\t\tif (statement == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tif (this.exception != null) {\n\t\t\t\tLOG.error(marker, DBKits.getSqlOfStatement(statement) + \", 发生异常\", this.exception);\n\t\t\t}\n\t\t\tstatement.close();\n\t\t\tint totalTime = (int) (System.currentTimeMillis() - beginTime);\n\t\t\tif (DBSettings.isUnionLogEnable() && (this.exception != null || totalTime >= DBSettings.unionLogTime())) {\n\t\t\t\tsqlLog.log(this, totalTime, this.exception);\n\t\t\t}\n\t\t\tthis.logSpendTime();\n\t\t} catch (SQLException e1) {\n\t\t\tLogs.db().error(e1.getMessage(), e1);\n\t\t}\n\t}\n\n\tprivate void logSpendTime() {\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tif (this.sqlTime > DBSettings.debugLogSpendTime()) {\n\t\t\t\tLOG.debug(marker, this.buildTimeLog());\n\t\t\t} else if (LOG.isTraceEnabled()) {\n\t\t\t\tLOG.trace(marker, this.buildTimeLog());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate String buildTimeLog() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"$耗时(ms):\").append(this.sqlTime);\n\t\tif (this.modifyCount >= 0) {\n\t\t\tsb.append(\",修改的记录数:\").append(this.modifyCount);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate void attachParams() {\n\t\tPreparedStatement statement = this.statement.get();\n\t\tif (statement == null) {\n\t\t\tthrow new SumkException(SumkExceptionCode.DB_CONNECTION_CLOSED, \"连接已关闭了\");\n\t\t}\n\t\tstatementParamAttacher.accept(statement, maped.getParamters());\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tLOG.debug(marker, sb.append(\"<=> \").append(DBKits.getSqlOfStatement(this.statement.get())).toString());\n\t\t}\n\t}\n\n\tpublic ResultSet getGeneratedKeys() throws SQLException {\n\t\tPreparedStatement statement = this.statement.get();\n\t\tif (statement == null) {\n\t\t\tthrow new SumkException(SumkExceptionCode.DB_CONNECTION_CLOSED, \"连接已关闭\");\n\t\t}\n\t\treturn statement.getGeneratedKeys();\n\t}\n\n\tpublic int getSqlTime() {\n\t\treturn sqlTime;\n\t}\n\n\tpublic int getModifyCount() {\n\t\treturn modifyCount;\n\t}\n\n\tpublic MapedSql getMaped() {\n\t\treturn maped;\n\t}\n\n\tpublic static long getExecuteCount() {\n\t\treturn executeCount;\n\t}\n}\n"
  },
  {
    "path": "sumk-db/src/main/java/org/yx/db/visit/Visitors.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.db.visit;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.db.conn.ConnectionPool;\nimport org.yx.db.enums.DBType;\nimport org.yx.db.event.DBEvent;\nimport org.yx.db.event.ModifyEvent;\nimport org.yx.db.sql.ColumnMeta;\nimport org.yx.db.sql.DBSettings;\nimport org.yx.db.sql.InsertResult;\nimport org.yx.db.sql.MapedSql;\nimport org.yx.db.sql.PojoMeta;\nimport org.yx.db.sql.SelectBuilder;\nimport org.yx.db.sql.SqlBuilder;\n\npublic final class Visitors {\n\n\tpublic static interface Transform<T> {\n\t\tT transFrom(ResultSet ret) throws Exception;\n\t}\n\n\tpublic static class QueryVisitor<T> implements SumkDbVisitor<T> {\n\t\tprivate final Transform<T> transform;\n\n\t\tprivate QueryVisitor(Transform<T> transform) {\n\t\t\tthis.transform = transform;\n\t\t}\n\n\t\t@Override\n\t\tpublic T visit(SqlBuilder builder) throws Exception {\n\t\t\tMapedSql maped = builder.toMapedSql();\n\t\t\tConnection conn = ConnectionPool.get().connection(DBSettings.readType());\n\t\t\ttry (SumkStatement statement = SumkStatement.create(conn, maped)) {\n\t\t\t\tResultSet ret = statement.executeQuery();\n\t\t\t\tT list = this.transform.transFrom(ret);\n\t\t\t\treturn list;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic static final SumkDbVisitor<Integer> modifyVisitor = builder -> {\n\t\tConnectionPool pool = ConnectionPool.get();\n\t\tMapedSql maped = builder.toMapedSql();\n\t\tint ret;\n\t\ttry (SumkStatement statement = SumkStatement.create(pool.connection(DBType.WRITE), maped)) {\n\t\t\tret = statement.executeUpdate();\n\t\t}\n\t\tif (ret > 0) {\n\t\t\tDBEvent md = maped.getEvent();\n\t\t\tif (md instanceof ModifyEvent) {\n\t\t\t\tModifyEvent me = (ModifyEvent) md;\n\t\t\t\tme.setAffected(ret);\n\t\t\t}\n\t\t\tpool.pubuishModify(md);\n\t\t}\n\t\treturn ret;\n\t};\n\n\tpublic static final SumkDbVisitor<InsertResult> insertWithAutoGeneratedKeysVisitor = builder -> {\n\t\tConnection conn = ConnectionPool.get().connection(DBType.WRITE);\n\t\tMapedSql maped = builder.toMapedSql();\n\t\ttry (SumkStatement statement = SumkStatement.createAutoGenerateKeyStatement(conn, maped)) {\n\t\t\tint count = statement.executeUpdate();\n\t\t\tResultSet rs = statement.getGeneratedKeys();\n\t\t\tList<Long> keys = new ArrayList<>();\n\t\t\tInsertResult result = new InsertResult(count, keys);\n\t\t\tif (rs != null && rs.getMetaData().getColumnCount() > 0) {\n\t\t\t\tif (rs.next()) {\n\t\t\t\t\tkeys.add(rs.getLong(1));\n\t\t\t\t}\n\t\t\t}\n\t\t\trs.close();\n\n\t\t\treturn result;\n\t\t}\n\t};\n\n\tpublic static final SumkDbVisitor<List<Map<ColumnMeta, Object>>> queryVisitorForORM = builder -> {\n\t\tMapedSql maped = builder.toMapedSql();\n\t\tConnection conn = ConnectionPool.get().connection(DBSettings.readType());\n\t\ttry (SumkStatement statement = SumkStatement.create(conn, maped)) {\n\t\t\tResultSet ret = statement.executeQuery();\n\t\t\tPojoMeta pm = ((SelectBuilder) builder).makeSurePojoMeta();\n\t\t\treturn ResultSetUtils.toMapList(ret, pm);\n\t\t}\n\t};\n\n\tpublic static final SumkDbVisitor<List<Map<String, Object>>> queryVisitor = new QueryVisitor<>(\n\t\t\tResultSetUtils::toMapList);\n\n\tpublic static final SumkDbVisitor<List<?>> singleListQueryVisitor = new QueryVisitor<>(ResultSetUtils::toList);\n\n\tpublic static final SumkDbVisitor<List<Object[]>> arrayListQueryVisitor = new QueryVisitor<>(\n\t\t\tResultSetUtils::toObjectArrayList);\n}\n"
  },
  {
    "path": "sumk-db/src/main/resources/META-INF/sql.dtd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\n<!ELEMENT sdb (sql+)>\n<!ATTLIST sdb\nnamespace CDATA #IMPLIED\n>\n\n<!ELEMENT sql (#PCDATA|if|ifnot|items|foreach)*>\n<!ATTLIST sql\nid ID #REQUIRED\n>\n\n<!ELEMENT if (#PCDATA|if|ifnot|items|foreach)*>\n<!ATTLIST if\ntest CDATA #REQUIRED\nfalseby (null|nokey|empty) #IMPLIED\n>\n\n<!ELEMENT ifnot (#PCDATA|if|ifnot|items|foreach)*>\n<!ATTLIST ifnot\ntest CDATA #REQUIRED\nfalseby (null|nokey|empty) #IMPLIED\n>\n\n<!ELEMENT items (if|ifnot|items|foreach)+>\n<!ATTLIST items\nseparator CDATA #REQUIRED\nopen CDATA #IMPLIED\nclose CDATA #IMPLIED\n>\n\n<!ELEMENT foreach (#PCDATA)>\n<!ATTLIST foreach\ncollection CDATA #REQUIRED\nseparator CDATA #REQUIRED\nitem CDATA #IMPLIED\nopen CDATA #IMPLIED\nclose CDATA #IMPLIED\n>"
  },
  {
    "path": "sumk-db/src/main/resources/META-INF/sumk.factories",
    "content": "sumk.ioc.bean=org.yx.db.conn.CommonConfigFactory,\\\norg.yx.db.exec.BoxAopExecutorSupplier,\\\norg.yx.db.listener.DeleteListener,\\\norg.yx.db.listener.InsertListener,\\\norg.yx.db.listener.SelectListener,\\\norg.yx.db.listener.UpdateListener,\\\norg.yx.db.mapper.DBPlugin\nsumk.ioc.boot.watcher=org.yx.db.sql.TableBootWatcher\n"
  },
  {
    "path": "sumk-framework/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright youtongluan (\\u6e38\\u901a\\u92ae\\uff0c\\u522b\\u540d\\uff1a\\u6e38\\u590f)\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "sumk-framework/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.github.youtongluan</groupId>\n\t\t<artifactId>sumk</artifactId>\n\t\t<version>4.2.1</version>\n\t</parent>\n\t<artifactId>sumk-framework</artifactId>\n\t<name>com.github.youtongluan:sumk</name>\n\t<description>A quick developing framewort for internet company</description>\n\t<url>https://github.com/youtongluan/sumk</url>\n\t<licenses>\n\t\t<license>\n\t\t\t<name>The Apache License, Version 2.0</name>\n\t\t\t<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n\t\t</license>\n\t</licenses>\n\t<scm>\n\t\t<url>https://github.com/youtongluan/sumk</url>\n\t\t<connection>https://github.com/youtongluan/sumk.git</connection>\n\t\t<developerConnection>https://github.com/youtongluan/sumk</developerConnection>\n\t</scm>\n\t<distributionManagement>\n\t    <repository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n\t    </repository>\n\t    <snapshotRepository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n\t    </snapshotRepository>\n\t</distributionManagement>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>sumk-base</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.ow2.asm</groupId>\n\t\t\t<artifactId>asm</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>redis.clients</groupId>\n\t\t\t<artifactId>jedis</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/Bean.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target({ ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Bean {\n\t/**\n\t * @return 自定义bean的名称。慎用此方法\n\t */\n\tString value() default \"\";\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/ConditionOnProperty.java",
    "content": "package org.yx.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 指定的属性名存在时该bean才会被初始化。属性指定是可以通过StartContext获取的属性。<BR>\n * 可以使用,或&amp;&amp;来表示依赖多个属性。用||来表示其中任何一个属性存在就可以初始化\n */\n@Target({ ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface ConditionOnProperty {\n\t/**\n\t * 可以用逗号或者||分隔。前者表示并且，后者表示或者\n\t * \n\t * @return StartContext或者AppInfo配置中的key\n\t */\n\tString value();\n\n\t/**\n\t * 默认情况下属性存在@Bean才有效，如果本方法返回false，那么属性不存在的时候，@Bean才有效\n\t * \n\t * @return 当onMatch和表达式的匹配结果一致时，条件成立\n\t */\n\tboolean onMatch() default true;\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/Exclude.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 本注解注释的接口，不会被<code>@SoaClient</code>解析。<BR>\n * 本注解注释的pojo字段，不会被映射到数据库。\n * 跟transient关键字效果类似，但是transient会影响S.json()、S.bean()，但是本注解不会\n */\n@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Exclude {\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/ExcludeFromParams.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 表示不作为参数。它对http和rpc的参数起作用\n */\n@Target({ ElementType.FIELD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface ExcludeFromParams {\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/ExcludeFromResponse.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 表示不作为响应内容。它对http和rpc的参数起作用\n */\n@Target({ ElementType.FIELD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface ExcludeFromResponse {\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/Inject.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 相当于spring的AutoWired，使用在全局变量上.它只会注入当前值为null的字段。<br>\n * 下列三种方式优先级从高到低注入对象，只有在上一种方式无法获取对象的时候，才使用下一种方式注入：<br>\n * 1、使用name和clz获取对象<br>\n * 2、使用name获取对象<br>\n * 3、使用clz获取对象<br>\n * \n * @author 游夏\n *\n */\n@Target({ ElementType.FIELD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Inject {\n\n\t/**\n\t * 如果设置了这个属性，会强制使用这个值去寻找bean，忽略掉智能推断。 该属性对数组或集合类型的注入无效\n\t * \n\t * @return 要注入的bean的名称\n\t */\n\tString value() default \"\";\n\n\t/**\n\t * 数组、集合类型不可能为null，但是可以为空\n\t * \n\t * @return 如果为true，表示可以为null或空集合\n\t */\n\tboolean allowEmpty() default false;\n\n\t/**\n\t * 是否允许存在多个bean。设置为true的时候，最好和Ordered接口一起使用\n\t * \n\t * @return 如果设置为true，存在多个bean的时候，返回第一个\n\t */\n\tboolean allowMulti() default false;\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/Param.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target({ ElementType.PARAMETER, ElementType.FIELD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Param {\n\n\t/**\n\t * @return 中文名称\n\t * \n\t */\n\tString value() default \"\";\n\n\t/**\n\t * 为了调试方便，可以在启动的时候设置sumk.param.required.enable=0来禁用它\n\t * \n\t * @return true表示是必传参数，不允许为null或空字符串\n\t */\n\tboolean required() default true;\n\n\t/**\n\t * 数字类型支持int、long、byte、short、Integer、Long、Byte、Short\n\t * \n\t * @return 字符串的最大长度或正整数的最大值(包含)，小于0表示不限\n\t */\n\tint max() default -1;\n\n\t/**\n\t * @return 字符串的最小长度或正整数的最小值(包含)，小于0表示不限\n\t */\n\tint min() default -1;\n\n\t/**\n\t * @return 文档中的字段示例，也可用于扩展\n\t */\n\tString example() default \"\";\n\n\t/**\n\t * @return 文档中的备注信息，也可用于扩展\n\t */\n\tString comment() default \"\";\n\n\t/**\n\t * 只有本属性为true的参数，才会校验内部字段<BR>\n\t * <B>不支持泛型，包括集合类型</B>。\n\t * \n\t * @return true表示该参数或字段是复合类型，会对内部的字段进行校验\n\t */\n\tboolean complex() default false;\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/Priority.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation;\n\nimport static java.lang.annotation.ElementType.TYPE;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\n@Target({ TYPE })\n@Retention(RUNTIME)\n@Documented\npublic @interface Priority {\n\n\t/**\n\t * bean解析时的优先级，值越小越靠前\n\t * \n\t * @return 如果没有用这个注解，默认为Const.DEFAULT_ORDER\n\t */\n\tint value();\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/spec/BeanSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.spec;\n\npublic class BeanSpec {\n\tprivate final String value;\n\tprivate final String conditionOnProperty;\n\t/**\n\t * conditionOnProperty不为空的时候，本属性才有意义\n\t */\n\tprivate final boolean onProperty;\n\n\tpublic BeanSpec(String value, String conditionOnProperty, boolean onProperty) {\n\t\tthis.value = value;\n\t\tthis.conditionOnProperty = conditionOnProperty;\n\t\tthis.onProperty = onProperty;\n\t}\n\n\tpublic String value() {\n\t\treturn this.value;\n\t}\n\n\tpublic String conditionOnProperty() {\n\t\treturn this.conditionOnProperty;\n\t}\n\n\tpublic boolean onProperty() {\n\t\treturn onProperty;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/spec/BuiltInParsers.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.spec;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.ConditionOnProperty;\nimport org.yx.annotation.Inject;\nimport org.yx.annotation.Param;\nimport org.yx.util.StringUtil;\n\npublic final class BuiltInParsers {\n\n\tpublic static final Function<Class<?>, BeanSpec> BEAN_PARSER = clz -> {\n\t\tBean c = clz.getAnnotation(Bean.class);\n\t\tif (c == null) {\n\t\t\treturn null;\n\t\t}\n\t\tConditionOnProperty p = clz.getAnnotation(ConditionOnProperty.class);\n\t\tif (p == null) {\n\t\t\treturn new BeanSpec(c.value(), null, true);\n\t\t}\n\t\treturn new BeanSpec(c.value(), StringUtil.toLatin(p.value()), p.onMatch());\n\t};\n\n\tpublic static final BiFunction<Object, Field, InjectSpec> INJECT_PARSER = (src, f) -> {\n\t\tInject c = f.getAnnotation(Inject.class);\n\t\tif (c == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new InjectSpec(c.value(), c.allowEmpty(), c.allowMulti());\n\t};\n\n\tpublic static final Function<Field, ParamSpec> PARAM_FIELD_PARSER = f -> {\n\t\tParam c = f.getAnnotation(Param.class);\n\t\tif (c == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn createParamSpec(c);\n\t};\n\n\tpublic static final Function<Method, ParamSpec[]> PARAM_PARAMTER_PARSER = m -> {\n\t\tAnnotation[][] paramAnno = m.getParameterAnnotations();\n\t\tParamSpec[] params = new ParamSpec[paramAnno.length];\n\t\tfor (int i = 0; i < paramAnno.length; i++) {\n\t\t\tAnnotation[] a = paramAnno[i];\n\t\t\tfor (Annotation a2 : a) {\n\t\t\t\tif (Param.class == a2.annotationType()) {\n\t\t\t\t\tparams[i] = createParamSpec((Param) a2);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn params;\n\t};\n\n\tpublic static ParamSpec createParamSpec(Param p) {\n\t\treturn new ParamSpec(p.value(), p.required(), p.max(), p.min(), p.example(), p.comment(), p.complex(), null);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/spec/InjectSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.spec;\n\npublic class InjectSpec {\n\tprivate final boolean allowEmpty;\n\tprivate final boolean allowMulti;\n\n\tprivate final String value;\n\n\tpublic InjectSpec(String value, boolean allowEmpty, boolean allowMulti) {\n\t\tthis.value = value;\n\t\tthis.allowEmpty = allowEmpty;\n\t\tthis.allowMulti = allowMulti;\n\t}\n\n\tpublic String value() {\n\t\treturn value;\n\t}\n\n\t/**\n\t * 数组、集合类型不可能为null，但是可以为空\n\t * \n\t * @return 如果为true，表示可以为null或空集合\n\t */\n\tpublic boolean allowEmpty() {\n\t\treturn this.allowEmpty;\n\t}\n\n\tpublic boolean allowMulti() {\n\t\treturn allowMulti;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/spec/ParamSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.spec;\n\nimport java.util.Objects;\n\npublic class ParamSpec {\n\n\tprivate final String value;\n\tprivate final boolean required;\n\tprivate final int max;\n\tprivate final int min;\n\tprivate final String example;\n\tprivate final String comment;\n\tprivate final boolean complex;\n\tprivate final Object custom;\n\n\tpublic ParamSpec(String value, boolean required, int max, int min, String example, String comment, boolean complex,\n\t\t\tObject custom) {\n\t\tthis.value = Objects.requireNonNull(value);\n\t\tthis.required = required;\n\t\tthis.max = max;\n\t\tthis.min = min;\n\t\tthis.example = example;\n\t\tthis.comment = comment;\n\t\tthis.complex = complex;\n\t\tthis.custom = custom;\n\t}\n\n\t/**\n\t * @return 中文名称\n\t * \n\t */\n\tpublic String value() {\n\t\treturn this.value;\n\t}\n\n\tpublic boolean required() {\n\t\treturn this.required;\n\t}\n\n\tpublic int max() {\n\t\treturn this.max;\n\t}\n\n\tpublic int min() {\n\t\treturn this.min;\n\t}\n\n\tpublic String example() {\n\t\treturn this.example;\n\t}\n\n\tpublic String comment() {\n\t\treturn this.comment;\n\t}\n\n\tpublic boolean complex() {\n\t\treturn this.complex;\n\t}\n\n\t/**\n\t * 这个属性是留给开发者自己扩展用\n\t * \n\t * @return 这个属性尽量不要返回空字符串，应用null代替空字符串\n\t */\n\tpublic Object custom() {\n\t\treturn this.custom;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/spec/SpecParsers.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.spec;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.Objects;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\npublic final class SpecParsers {\n\n\tprivate static Function<Class<?>, BeanSpec> beanParser = BuiltInParsers.BEAN_PARSER;\n\tprivate static BiFunction<Object, Field, InjectSpec> injectParser = BuiltInParsers.INJECT_PARSER;\n\n\tprivate static Function<Field, ParamSpec> paramFieldParser = BuiltInParsers.PARAM_FIELD_PARSER;\n\n\tprivate static Function<Method, ParamSpec[]> paramParamterParser = BuiltInParsers.PARAM_PARAMTER_PARSER;\n\n\tpublic static Function<Class<?>, BeanSpec> getBeanParser() {\n\t\treturn beanParser;\n\t}\n\n\tpublic static BiFunction<Object, Field, InjectSpec> getInjectParser() {\n\t\treturn injectParser;\n\t}\n\n\tpublic static Function<Field, ParamSpec> getParamFieldParser() {\n\t\treturn paramFieldParser;\n\t}\n\n\tpublic static Function<Method, ParamSpec[]> getParamParamterParser() {\n\t\treturn paramParamterParser;\n\t}\n\n\tpublic static void setBeanParser(Function<Class<?>, BeanSpec> beanParser) {\n\t\tSpecParsers.beanParser = Objects.requireNonNull(beanParser);\n\t}\n\n\tpublic static void setInjectParser(BiFunction<Object, Field, InjectSpec> injectParser) {\n\t\tSpecParsers.injectParser = Objects.requireNonNull(injectParser);\n\t}\n\n\tpublic static void setParamFieldParser(Function<Field, ParamSpec> paramFieldParser) {\n\t\tSpecParsers.paramFieldParser = Objects.requireNonNull(paramFieldParser);\n\t}\n\n\tpublic static void setParamParamterParser(Function<Method, ParamSpec[]> paramParamterParser) {\n\t\tSpecParsers.paramParamterParser = Objects.requireNonNull(paramParamterParser);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/annotation/spec/Specs.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.spec;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\npublic class Specs {\n\n\tpublic static BeanSpec extractBean(Class<?> clz) {\n\t\treturn parse(clz, SpecParsers.getBeanParser());\n\t}\n\n\tpublic static InjectSpec extractInject(Object destBean, Field f) {\n\t\treturn parse(destBean, f, SpecParsers.getInjectParser());\n\t}\n\n\tpublic static ParamSpec extractParamField(Field f) {\n\t\treturn parse(f, SpecParsers.getParamFieldParser());\n\t}\n\n\tpublic static ParamSpec[] extractParamParamter(Method m) {\n\t\treturn parse(m, SpecParsers.getParamParamterParser());\n\t}\n\n\tprotected static <T, R> R parse(T t, Function<T, R> parser) {\n\t\treturn parser.apply(t);\n\t}\n\n\tprotected static <T, U, R> R parse(T t, U u, BiFunction<T, U, R> parser) {\n\t\treturn parser.apply(t, u);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/BeanAssemblerBootWatcher.java",
    "content": "package org.yx.bean;\n\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.yx.annotation.spec.BeanSpec;\nimport org.yx.annotation.spec.Specs;\nimport org.yx.common.util.kit.PriorityKits;\nimport org.yx.log.Logs;\n\npublic class BeanAssemblerBootWatcher extends ParallelBootWatcher {\n\n\t@Override\n\tprotected void handle(Class<?> clz) throws Exception {\n\t\tBeanSpec b = Specs.extractBean(clz);\n\t\tBeanRegistry.registerClass(clz, b);\n\t}\n\n\tprotected List<List<Class<?>>> priorityList(List<Class<?>> sortedClasses) {\n\t\treturn PriorityKits.split(sortedClasses);\n\t}\n\n\t@Override\n\tpublic List<Class<?>> publish(List<Class<?>> sortedClasses, Predicate<String> optional) throws Exception {\n\t\tList<List<Class<?>>> lists = priorityList(sortedClasses);\n\t\tthis.serialPublish(lists.get(0), optional);\n\t\tLogs.ioc().trace(\"handled lower bean classes:{}\", lists.get(0));\n\t\tsuper.publish(lists.get(1), optional);\n\t\tthis.serialPublish(lists.get(2), optional);\n\t\tLogs.ioc().trace(\"handled higher bean classes:{}\", lists.get(2));\n\t\tLogs.ioc().debug(\"full beans:{}\", InnerIOC.beans());\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn 1000;\n\t}\n\n}"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/BeanFieldFinder.java",
    "content": "package org.yx.bean;\n\nimport java.lang.reflect.Field;\n\nimport org.yx.annotation.spec.InjectSpec;\n\n/**\n * 寻找bean里面需要注入的字段的对象。\n */\npublic interface BeanFieldFinder {\n\t/**\n\t * 查找要注入该字段的对象\n\t * \n\t * @param f      bean中待注入的field\n\t * @param bean   字段所在的bean\n\t * @param inject 注入属性\n\t * @return 将要被注入该field的对象，可能是单个对象，也可能是数组或List。查找不到就返回null\n\t * @throws Exception 如果抛出异常，就表示启动失败\n\t */\n\tObject findTarget(Field f, Object bean, InjectSpec inject) throws Exception;\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/BeanKit.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.lang.reflect.Modifier;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\npublic final class BeanKit {\n\n\tpublic static Class<?> getTargetClass(Object bean) {\n\t\treturn bean instanceof Boxed ? ((Boxed) bean).targetRawClass() : bean.getClass();\n\t}\n\n\tpublic static String resloveBeanName(Class<?> clz) {\n\t\tString name = StringUtil.uncapitalize(clz.getSimpleName());\n\t\tif (name.endsWith(\"Impl\")) {\n\t\t\tname = name.substring(0, name.length() - 4);\n\t\t}\n\t\treturn name;\n\t}\n\n\tpublic static Set<String> resloveBeanNames(Class<?> clazz) {\n\t\tSet<Class<?>> interfaces = new HashSet<>();\n\t\tresloveSuperClassAndInterface(clazz, interfaces);\n\t\tSet<String> names = new HashSet<>();\n\t\tfor (Class<?> clz : interfaces) {\n\t\t\tnames.add(resloveBeanName(clz));\n\t\t}\n\t\treturn names;\n\t}\n\n\tprivate static void resloveSuperClassAndInterface(Class<?> clazz, Set<Class<?>> interfaces) {\n\t\twhile (clazz != null && !clazz.getName().startsWith(Loader.JAVA_PRE)\n\t\t\t\t&& (clazz.getModifiers() & Modifier.PUBLIC) != 0) {\n\t\t\tinterfaces.add(clazz);\n\t\t\tClass<?>[] ifcs = clazz.getInterfaces();\n\t\t\tif (ifcs != null) {\n\t\t\t\tfor (Class<?> ifc : ifcs) {\n\t\t\t\t\tresloveSuperClassAndInterface(ifc, interfaces);\n\t\t\t\t}\n\t\t\t}\n\t\t\tclazz = clazz.getSuperclass();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/BeanPool.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.IdentityHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassWriter;\nimport org.yx.bean.aop.AopContext;\nimport org.yx.bean.aop.AopExecutor;\nimport org.yx.bean.aop.AopExecutorManager;\nimport org.yx.bean.aop.asm.AsmUtils;\nimport org.yx.bean.aop.asm.ProxyClassVistor;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.log.Logs;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\npublic final class BeanPool {\n\n\tprivate final ConcurrentMap<String, NameSlot> slotMap = new ConcurrentHashMap<>(128);\n\n\tprivate Class<?> proxyIfNeed(Class<?> clz) throws Exception {\n\n\t\tMethod[] methods = clz.getDeclaredMethods();\n\t\tMap<Method, Integer> aopMethods = new HashMap<>();\n\t\tAopExecutorManager manager = AopExecutorManager.get();\n\t\tfor (Method m : methods) {\n\t\t\tint modifier = m.getModifiers();\n\t\t\tif (!AsmUtils.canProxy(modifier)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!Modifier.isPublic(modifier) && !Modifier.isProtected(modifier)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tList<AopContext> list = manager.willProxyExcutorSuppliers(clz, m);\n\t\t\tif (list.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tClass<?> returnType = m.getReturnType();\n\t\t\tif (returnType != null && returnType != Void.TYPE && returnType.isPrimitive()) {\n\t\t\t\tLogs.aop().warn(\"{}.{}()的返回值是{}，这个方法aop无法屏蔽异常！\", clz.getName(), m.getName(), returnType);\n\t\t\t}\n\t\t\tint index = manager.indexSupplier(list);\n\t\t\taopMethods.put(m, index);\n\t\t\tif (Logs.aop().isDebugEnabled()) {\n\t\t\t\tList<AopExecutor> aopList = new ArrayList<>(list.size());\n\t\t\t\tfor (AopContext c : list) {\n\t\t\t\t\taopList.add(c.getAopExecutor());\n\t\t\t\t}\n\t\t\t\tLogs.aop().debug(\"{}.{}被{}代理了,序号:{}\", clz.getName(), m.getName(), aopList, index);\n\t\t\t}\n\t\t}\n\n\t\tif (aopMethods.isEmpty()) {\n\t\t\treturn clz;\n\t\t}\n\n\t\tClassReader cr = new ClassReader(AsmUtils.openStreamForClass(clz));\n\n\t\tClassWriter cw = new ClassWriter(cr, AppInfo.getInt(\"sumk.asm.writer.box.compute\", ClassWriter.COMPUTE_MAXS));\n\n\t\tString newClzName = AsmUtils.proxyCalssName(clz);\n\t\tProxyClassVistor cv = new ProxyClassVistor(cw, newClzName, clz, aopMethods);\n\t\tcr.accept(cv, AsmUtils.asmVersion());\n\t\treturn AsmUtils.defineClass(newClzName, cw.toByteArray(), clz.getClassLoader());\n\t}\n\n\tpublic List<String> beanNames() {\n\t\tSet<String> names = slotMap.keySet();\n\t\treturn new ArrayList<>(names);\n\t}\n\n\tpublic Collection<Object> beans() {\n\t\tMap<Object, Boolean> beans = new IdentityHashMap<>(slotMap.size());\n\t\tfor (NameSlot v : slotMap.values()) {\n\t\t\tfor (Object bean : v.beans()) {\n\t\t\t\tbeans.putIfAbsent(bean, Boolean.TRUE);\n\t\t\t}\n\t\t}\n\t\treturn beans.keySet();\n\t}\n\n\tpublic <T> T putClass(String beanName, Class<T> clz) throws Exception {\n\t\tObjects.requireNonNull(clz);\n\t\tCollection<String> names = (beanName == null || (beanName = beanName.trim()).isEmpty())\n\t\t\t\t? BeanKit.resloveBeanNames(clz)\n\t\t\t\t: StringUtil.splitAndTrim(beanName, Const.COMMA, Const.SEMICOLON);\n\t\tif (names == null || names.isEmpty()) {\n\t\t\tnames = Collections.singleton(BeanKit.resloveBeanName(clz));\n\t\t}\n\t\tClass<?> proxyClz = this.proxyIfNeed(clz);\n\t\tObject bean = Loader.newInstance(proxyClz);\n\t\tfor (String name : names) {\n\t\t\tput(name, bean);\n\t\t}\n\t\treturn this.getBean(beanName, clz);\n\t}\n\n\tpublic <T> T putBean(String beanName, T bean) {\n\t\tClass<?> clz = Objects.requireNonNull(bean).getClass();\n\t\tCollection<String> names = (beanName == null || (beanName = beanName.trim()).isEmpty())\n\t\t\t\t? BeanKit.resloveBeanNames(clz)\n\t\t\t\t: StringUtil.splitAndTrim(beanName, Const.COMMA, Const.SEMICOLON);\n\t\tif (names == null || names.isEmpty()) {\n\t\t\tnames = Collections.singleton(BeanKit.resloveBeanName(clz));\n\t\t}\n\t\tfor (String name : names) {\n\t\t\tput(name, bean);\n\t\t}\n\t\treturn bean;\n\t}\n\n\tprivate synchronized boolean put(String name, Object bean) {\n\t\tLogs.ioc().debug(\"add bean {} : {}\", name, bean);\n\t\tNameSlot oldSlot = slotMap.putIfAbsent(name, new NameSlot(name, new Object[] { bean }));\n\t\tif (oldSlot == null) {\n\t\t\treturn true;\n\t\t}\n\t\treturn oldSlot.appendBean(bean);\n\t}\n\n\tpublic <T> T getBean(String name, Class<T> clz) {\n\t\tif (name == null || name.length() == 0) {\n\t\t\tname = BeanKit.resloveBeanName(clz);\n\t\t}\n\t\tNameSlot bw = slotMap.get(name);\n\t\treturn bw == null ? null : bw.getBean(clz);\n\t}\n\n\tpublic <T> List<T> getBeans(String name, Class<T> clz) {\n\t\tif (name == null || name.length() == 0) {\n\t\t\tname = BeanKit.resloveBeanName(clz);\n\t\t}\n\t\tNameSlot slot = slotMap.get(name);\n\t\treturn slot == null ? Collections.emptyList() : slot.getBeans(clz);\n\n\t}\n\n\tpublic NameSlot getSlot(String name) {\n\t\treturn slotMap.get(name);\n\t}\n\n\tpublic void clear() {\n\t\tslotMap.clear();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn slotMap.toString();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/BeanProvider.java",
    "content": "package org.yx.bean;\n\nimport java.util.List;\n\npublic interface BeanProvider {\n\t<T> List<T> getBeans(String name, Class<T> clz);\n\n\tList<String> beanNames();\n\n\t<T> T getBean(String name, Class<T> clz);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/BeanRegistry.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.util.Collection;\n\nimport org.yx.annotation.spec.BeanSpec;\nimport org.yx.base.context.AppContext;\nimport org.yx.common.Predicator;\nimport org.yx.log.Logs;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\npublic class BeanRegistry {\n\n\tpublic static void registerClass(Class<?> clz, BeanSpec spec) throws Exception {\n\t\tif (spec == null || !valid(clz, spec)) {\n\t\t\treturn;\n\t\t}\n\t\tif (FactoryBean.class.isAssignableFrom(clz)) {\n\t\t\tFactoryBean factory = (FactoryBean) Loader.newInstance(clz);\n\t\t\tregisterBeans(factory.beans());\n\t\t} else {\n\t\t\tInnerIOC.putClass(spec.value(), clz);\n\t\t}\n\t}\n\n\tpublic static boolean valid(Class<?> clz, BeanSpec bean) {\n\t\tString v = bean.conditionOnProperty();\n\t\tif (StringUtil.isNotEmpty(v)) {\n\t\t\tboolean match = bean.onProperty();\n\t\t\tboolean exist = Predicator.test(v, name -> AppContext.inst().getAppInfo(name, null) != null);\n\t\t\tif (match ^ exist) {\n\t\t\t\tLogs.system().info(\"{} exclude because conditionOnProperty donot meet.on match:{},exist:{}\",\n\t\t\t\t\t\tclz.getName(), match, exist);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic static void registerBeans(Collection<?> beans) throws Exception {\n\t\tif (beans != null && beans.size() > 0) {\n\t\t\tfor (Object obj : beans) {\n\t\t\t\tregisterBean(null, obj);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void registerBean(String name, Object obj) throws Exception {\n\t\tif (obj == null) {\n\t\t\treturn;\n\t\t}\n\t\tClass<?> clz = obj.getClass();\n\t\tif (clz == Class.class) {\n\t\t\tInnerIOC.putClass(name, (Class<?>) obj);\n\t\t\treturn;\n\t\t}\n\n\t\tif (clz == NamedBean.class) {\n\t\t\tNamedBean named = (NamedBean) obj;\n\t\t\tregisterBean(named.getBeanName(), named.getBean());\n\t\t\treturn;\n\t\t}\n\n\t\tif (clz == InterfaceBean.class) {\n\t\t\tInterfaceBean complex = (InterfaceBean) obj;\n\t\t\tregisterBean(BeanKit.resloveBeanName(complex.getIntf()), complex.getBean());\n\t\t\treturn;\n\t\t}\n\n\t\tInnerIOC.putBean(name, obj);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/Booter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.slf4j.Logger;\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.spec.InjectSpec;\nimport org.yx.annotation.spec.Specs;\nimport org.yx.base.context.AppContext;\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.base.matcher.Matchers;\nimport org.yx.base.scaner.ClassScaner;\nimport org.yx.bean.aop.AopExecutorManager;\nimport org.yx.bean.aop.AopExecutorSupplier;\nimport org.yx.bean.aop.asm.AsmUtils;\nimport org.yx.bean.watcher.BeanCreateWatcher;\nimport org.yx.bean.watcher.BeanInjectWatcher;\nimport org.yx.bean.watcher.BootWatcher;\nimport org.yx.common.util.kit.PriorityKits;\nimport org.yx.conf.Const;\nimport org.yx.exception.SimpleSumkException;\nimport org.yx.log.Logs;\nimport org.yx.main.SumkServer;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\npublic final class Booter {\n\n\tprivate final Logger logger = Logs.ioc();\n\n\tprivate static final String FACTORIES_FILE = \"META-INF/sumk.factories\";\n\tprivate static final String SUMK_IOC_EXCLUDE = \"sumk.ioc.exclude\";\n\t/**\n\t * 这里定义精确扫描的类，这些类也需要@Bean注解\n\t */\n\tprivate static final String SUMK_IOC_BEAN = \"sumk.ioc.bean\";\n\n\tprivate static final String SUMK_IOC_BOOT_WATCHER = \"sumk.ioc.boot.watcher\";\n\t/**\n\t * 符合这里表达式的类，即使加载出错，即使出了问题，也认为是正常的。 这里的加载出错指的是class文件能找到，但是它的关联类加载出错\n\t */\n\tprivate static final String SUMK_IOC_OPTIONAL = \"sumk.ioc.optional\";\n\n\tpublic void start(List<String> packageNames) throws Exception {\n\t\tonStart(packageNames);\n\t\tBeanScanerInstance definition = this.buildBeanScanerInstance(packageNames);\n\t\tList<Class<?>> orignClazzList = this.loadAndSortClasses(definition);\n\t\tList<BootWatcher> watchers = loadWatcher(definition);\n\t\tthis.refresh(orignClazzList, watchers, definition);\n\t\tthis.autoWiredAll(definition);\n\t\tnew PluginBooter().start();\n\t\tRuntime.getRuntime().addShutdownHook(new Thread(SumkServer::destroy));\n\t}\n\n\tprivate BeanScanerInstance buildBeanScanerInstance(List<String> packageNames) throws IOException {\n\t\tFactoriesInstance fi = this.parseFactoriesInstance();\n\t\tBeanScanerInstance definition = new BeanScanerInstance(packageNames, fi);\n\t\tlogger.debug(\"finally bean scan instance:{}\", definition);\n\t\treturn definition;\n\t}\n\n\tprivate void onStart(List<String> packageNames) {\n\n\t\tAopExecutorManager.reset();\n\t\tInnerIOC.clear();\n\t\tAsmUtils.clearProxyClassLoaders();\n\t\tif (packageNames.isEmpty()) {\n\t\t\tlogger.warn(\"property [sumk.ioc] is empty\");\n\t\t\treturn;\n\t\t}\n\t\tlogger.debug(\"start ioc with packages:[{}]\", packageNames);\n\t}\n\n\t/**\n\t * 包含了从配置以及jar中的meta文件加载的\n\t * \n\t * @return\n\t * @throws IOException\n\t */\n\tprivate FactoriesInstance parseFactoriesInstance() throws IOException {\n\t\tFactoriesInstance ret = FactoriesInstance.fromContext();\n\t\tEnumeration<URL> urls = Loader.getResources(FACTORIES_FILE);\n\t\twhile (urls.hasMoreElements()) {\n\t\t\tURL url = urls.nextElement();\n\t\t\ttry (InputStream is = url.openStream()) {\n\t\t\t\tProperties properties = new Properties();\n\t\t\t\tproperties.load(is);\n\t\t\t\tFactoriesInstance f = FactoriesInstance.fromProperties(properties);\n\t\t\t\tlogger.debug(\"factories [{}]: {}\", url, f);\n\t\t\t\tret.addAll(f);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\tprivate List<Class<?>> unmodifyClassList(List<Class<?>> list) {\n\t\treturn CollectionUtil.unmodifyList(list.toArray(new Class<?>[list.size()]));\n\t}\n\n\tprivate void refresh(List<Class<?>> clazzList, List<BootWatcher> watchers, BeanScanerInstance definition)\n\t\t\tthrows Exception {\n\t\tclazzList = unmodifyClassList(clazzList);\n\t\twatchers.sort(null);\n\t\tfor (BootWatcher b : watchers) {\n\t\t\tList<Class<?>> temp = b.publish(clazzList, definition.optionalMatcher);\n\t\t\tif (temp != null) {\n\t\t\t\tclazzList = unmodifyClassList(temp);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate List<Class<?>> loadAndSortClasses(BeanScanerInstance definition) throws Exception {\n\t\tCollection<String> clzsFromPackage = ClassScaner.listClasses(definition.packageNames);\n\t\tSet<String> clzs = new HashSet<>(clzsFromPackage);\n\t\tif (CollectionUtil.isNotEmpty(definition.beans)) {\n\t\t\tclzs.addAll(definition.beans);\n\t\t}\n\t\tList<Class<?>> clazzList = new ArrayList<>(clzs.size());\n\n\t\tfor (String c : clzs) {\n\t\t\tClass<?> clz = this.loadClass(c, definition);\n\t\t\tif (clz == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (AopExecutorSupplier.class.isAssignableFrom(clz) && clz.isAnnotationPresent(Bean.class)) {\n\t\t\t\tInnerIOC.putBean(null, Loader.newInstance(clz));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tclazzList.add(clz);\n\t\t}\n\n\t\tList<Class<?>> sortedClazzList = PriorityKits.sort(clazzList);\n\t\tthis.printClassListDebugInfo(sortedClazzList);\n\t\treturn sortedClazzList;\n\t}\n\n\tprivate void printClassListDebugInfo(List<Class<?>> sortedClazzList) {\n\t\tif (!logger.isDebugEnabled()) {\n\t\t\treturn;\n\t\t}\n\t\tlogger.debug(\"scan class size:{}, {} {}..{} {}\", sortedClazzList.size(), sortedClazzList.get(0).getSimpleName(),\n\t\t\t\tsortedClazzList.get(1).getSimpleName(), sortedClazzList.get(sortedClazzList.size() - 2).getSimpleName(),\n\t\t\t\tsortedClazzList.get(sortedClazzList.size() - 1).getSimpleName());\n\t\tlogger.trace(\"ordered class:\\n{}\", sortedClazzList);\n\t}\n\n\tprivate List<BootWatcher> loadWatcher(BeanScanerInstance definition) throws Exception {\n\t\tSet<String> ws = definition.watchers;\n\t\tif (CollectionUtil.isEmpty(ws)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<BootWatcher> watchers = new ArrayList<>();\n\t\tfor (String w : ws) {\n\t\t\tClass<?> bw = this.loadClass(w, definition);\n\t\t\tif (bw == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!BootWatcher.class.isAssignableFrom(bw)) {\n\t\t\t\tlogger.error(\"[{}] must implement BootWatcher\", bw);\n\t\t\t\tAppContext.startFailed();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twatchers.add((BootWatcher) Loader.newInstance(bw));\n\t\t}\n\t\treturn watchers;\n\t}\n\n\tprivate Class<?> loadClass(String fullName, BeanScanerInstance definition) {\n\t\tif (definition.excludeMatcher.test(fullName)) {\n\t\t\tlogger.info(\"[{}] ingored by [{}]\", fullName, SUMK_IOC_EXCLUDE);\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(\"{} begin loading\", fullName);\n\t\t\t}\n\n\t\t\tClass<?> clz = Loader.loadClass(fullName);\n\t\t\tif ((clz.getModifiers() & (Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL | Modifier.PUBLIC\n\t\t\t\t\t| Modifier.INTERFACE)) != Modifier.PUBLIC || clz.isAnonymousClass() || clz.isLocalClass()\n\t\t\t\t\t|| clz.isAnnotation() || clz.isEnum()) {\n\t\t\t\tlogger.trace(\"{} ingored because it is not normal public class\", fullName);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn clz;\n\t\t} catch (Throwable e) {\n\t\t\tif ((e instanceof LinkageError) && definition.optionalMatcher.test(fullName)) {\n\t\t\t\tlogger.debug(\"{} ignored when load because: [{}]\", fullName, e);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tlogger.error(fullName + \"加载失败\", e);\n\t\t\tAppContext.startFailed();\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate void injectField(Field f, Object bean, Object target) throws IllegalAccessException {\n\t\tif (!f.isAccessible()) {\n\t\t\tf.setAccessible(true);\n\t\t}\n\t\tf.set(bean, target);\n\t}\n\n\tprivate void autoWiredAll(BeanScanerInstance definition) throws Exception {\n\t\tList<Object> beans = CollectionUtil.unmodifyList(InnerIOC.beans().toArray());\n\t\tlogger.trace(\"after beans create...\");\n\t\tfor (BeanCreateWatcher w : IOC.getBeans(BeanCreateWatcher.class)) {\n\t\t\tw.afterCreate(beans);\n\t\t}\n\t\tlogger.trace(\"inject beans properties...\");\n\t\tbeans = CollectionUtil.unmodifyList(InnerIOC.beans().toArray());\n\t\tfor (Object bean : beans) {\n\t\t\tinjectProperties(bean, definition);\n\t\t}\n\t\tlogger.trace(\"after beans installed...\");\n\t\tfor (BeanInjectWatcher watcher : IOC.getBeans(BeanInjectWatcher.class)) {\n\t\t\twatcher.afterInject(beans);\n\t\t}\n\t\tlogger.trace(\"plugins starting...\");\n\t}\n\n\tprivate void injectProperties(Object bean, BeanScanerInstance definition) throws Exception {\n\t\tClass<?> tempClz = bean.getClass();\n\t\twhile (tempClz != null && (!tempClz.getName().startsWith(Loader.JAVA_PRE))) {\n\n\t\t\tField[] fs = tempClz.getDeclaredFields();\n\t\t\tfor (Field f : fs) {\n\t\t\t\tInjectSpec inject = Specs.extractInject(bean, f);\n\t\t\t\tif (inject == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tObject target = definition.fieldFinder.findTarget(f, bean, inject);\n\t\t\t\tif (target == null) {\n\t\t\t\t\tif (inject.allowEmpty()) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthrow new SimpleSumkException(235435658, bean.getClass().getName() + \".\" + f.getName()\n\t\t\t\t\t\t\t+ \" cannot injected. because bean not founds\");\n\t\t\t\t}\n\t\t\t\tinjectField(f, bean, target);\n\t\t\t}\n\t\t\ttempClz = tempClz.getSuperclass();\n\t\t}\n\t}\n\n\tprivate static final class FactoriesInstance {\n\n\t\tfinal Set<String> beans = new HashSet<>();\n\n\t\tfinal Set<String> bootWatchers = new HashSet<>();\n\n\t\tfinal Set<String> excludes = new HashSet<>();\n\n\t\tfinal Set<String> optionals = new HashSet<>();\n\n\t\tprivate static void addValues(Set<String> set, String factoryNamesProperty) {\n\t\t\tif (StringUtil.isEmpty(factoryNamesProperty)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tList<String> terms = StringUtil.splitAndTrim(factoryNamesProperty, Const.COMMA);\n\t\t\tset.addAll(terms);\n\t\t}\n\n\t\tpublic void addAll(FactoriesInstance f) {\n\t\t\tthis.beans.addAll(f.beans);\n\t\t\tthis.bootWatchers.addAll(f.bootWatchers);\n\t\t\tthis.excludes.addAll(f.excludes);\n\t\t\tthis.optionals.addAll(f.optionals);\n\t\t}\n\n\t\tpublic static FactoriesInstance fromProperties(Properties properties) {\n\t\t\tFactoriesInstance fi = new FactoriesInstance();\n\t\t\taddValues(fi.beans, properties.getProperty(SUMK_IOC_BEAN));\n\t\t\taddValues(fi.bootWatchers, properties.getProperty(SUMK_IOC_BOOT_WATCHER));\n\t\t\taddValues(fi.excludes, properties.getProperty(SUMK_IOC_EXCLUDE));\n\t\t\taddValues(fi.optionals, properties.getProperty(SUMK_IOC_OPTIONAL));\n\t\t\treturn fi;\n\t\t}\n\n\t\tpublic static FactoriesInstance fromContext() {\n\t\t\tFactoriesInstance fi = new FactoriesInstance();\n\t\t\taddValues(fi.beans, AppContext.inst().getAppInfo(SUMK_IOC_BEAN, \"\"));\n\t\t\taddValues(fi.bootWatchers, AppContext.inst().getAppInfo(SUMK_IOC_BOOT_WATCHER, \"\"));\n\t\t\taddValues(fi.excludes, AppContext.inst().getAppInfo(SUMK_IOC_EXCLUDE, \"\"));\n\t\t\taddValues(fi.optionals, AppContext.inst().getAppInfo(SUMK_IOC_OPTIONAL, \"\"));\n\t\t\treturn fi;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"FactoriesInstance [beans=\" + beans + \", bootWatchers=\" + bootWatchers + \", excludes=\" + excludes\n\t\t\t\t\t+ \", optionals=\" + optionals + \"]\";\n\t\t}\n\n\t}\n\n\tprivate static final class BeanScanerInstance {\n\n\t\tfinal BeanFieldFinder fieldFinder = AppContext.inst().get(BeanFieldFinder.class, new DefaultBeanFieldFinder());\n\n\t\tfinal List<String> packageNames;\n\n\t\tfinal Set<String> beans;\n\n\t\tfinal Set<String> watchers;\n\n\t\tfinal Predicate<String> excludeMatcher;\n\n\t\tfinal Predicate<String> optionalMatcher;\n\n\t\tpublic BeanScanerInstance(List<String> packageNames, FactoriesInstance fi) {\n\t\t\tthis.packageNames = packageNames;\n\t\t\tthis.beans = fi.beans;\n\t\t\tthis.watchers = fi.bootWatchers;\n\t\t\tthis.excludeMatcher = createWildcardMatcher(fi.excludes);\n\t\t\tthis.optionalMatcher = createWildcardMatcher(fi.optionals);\n\t\t}\n\n\t\tprivate Predicate<String> createWildcardMatcher(Collection<String> patterns) {\n\t\t\tif (CollectionUtil.isEmpty(patterns)) {\n\t\t\t\treturn BooleanMatcher.FALSE;\n\t\t\t}\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tfor (String v : patterns) {\n\t\t\t\tsb.append(v).append(Matchers.SPLIT);\n\t\t\t}\n\t\t\treturn Matchers.createWildcardMatcher(sb.toString(), 2);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"BeanScanerInstance [packageNames=\" + packageNames + \", excludeMatcher=\" + excludeMatcher\n\t\t\t\t\t+ \", optionalMatcher=\" + optionalMatcher + \", beans=\" + beans + \", watchers=\" + watchers + \"]\";\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/Boxed.java",
    "content": "package org.yx.bean;\n\npublic interface Boxed {\n\n\tClass<?> targetRawClass();\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/ComplexBean.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\npublic interface ComplexBean {\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/DefaultBeanFieldFinder.java",
    "content": "package org.yx.bean;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Field;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.yx.annotation.spec.InjectSpec;\nimport org.yx.exception.SimpleSumkException;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.Loader;\n\npublic class DefaultBeanFieldFinder implements BeanFieldFinder {\n\n\t@Override\n\tpublic Object findTarget(Field f, Object bean, InjectSpec inject) throws Exception {\n\t\tClass<?> fieldType = f.getType();\n\n\t\tif (fieldType.isArray()) {\n\t\t\treturn getArrayField(f, bean, inject.allowEmpty());\n\t\t}\n\t\tif (List.class == fieldType || Collection.class == fieldType) {\n\t\t\treturn getListField(f, bean, inject.allowEmpty());\n\t\t}\n\n\t\treturn getSimpleBean(f, inject);\n\t}\n\n\tprotected Object getSimpleBean(Field f, InjectSpec inject) {\n\t\tClass<?> clz = f.getType();\n\t\tString name = inject.value();\n\n\t\tif (name != null && (name = name.trim()).length() > 0) {\n\t\t\treturn IOC.get(name, clz);\n\t\t}\n\n\t\tList<?> list = IOC.getBeans(f.getName(), clz);\n\t\tif (list.size() == 1) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\tif (list.size() > 1) {\n\t\t\tfor (Object obj : list) {\n\n\t\t\t\tif (clz == BeanKit.getTargetClass(obj)) {\n\t\t\t\t\treturn obj;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (inject.allowMulti()) {\n\t\t\t\treturn list.get(0);\n\t\t\t}\n\t\t}\n\t\tif (inject.allowMulti()) {\n\t\t\tlist = IOC.getBeans(null, clz);\n\t\t\tif (list.size() > 0) {\n\t\t\t\treturn list.get(0);\n\t\t\t}\n\t\t}\n\t\treturn IOC.get(clz);\n\t}\n\n\tprotected List<?> getListField(Field f, Object bean, boolean allowEmpty) throws ClassNotFoundException {\n\t\tString genericName = f.getGenericType().getTypeName();\n\t\tif (genericName == null || genericName.isEmpty() || !genericName.contains(\"<\")) {\n\t\t\tthrow new SimpleSumkException(239845611,\n\t\t\t\t\tbean.getClass().getName() + \".\" + f.getName() + \"is List,but not List<T>\");\n\t\t}\n\t\tgenericName = genericName.substring(genericName.indexOf(\"<\") + 1, genericName.length() - 1);\n\t\tClass<?> clz = Loader.loadClass(genericName);\n\t\tif (clz == Object.class) {\n\t\t\tthrow new SimpleSumkException(23984568,\n\t\t\t\t\tbean.getClass().getName() + \".\" + f.getName() + \": beanClz of @Inject in list type cannot be null\");\n\t\t}\n\t\tList<?> target = IOC.getBeans(clz);\n\t\tif (target == null || target.isEmpty()) {\n\t\t\tif (!allowEmpty) {\n\t\t\t\tthrow new SimpleSumkException(235435652, bean.getClass().getName() + \".\" + f.getName() + \" is empty.\");\n\t\t\t}\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn CollectionUtil.unmodifyList(target.toArray());\n\t}\n\n\tprotected Object[] getArrayField(Field f, Object bean, boolean allowEmpty) {\n\t\tClass<?> clz = f.getType().getComponentType();\n\t\tList<?> target = IOC.getBeans(clz);\n\t\tif (target == null || target.isEmpty()) {\n\t\t\tif (!allowEmpty) {\n\t\t\t\tthrow new SimpleSumkException(235435651, bean.getClass().getName() + \".\" + f.getName() + \" is empty.\");\n\t\t\t}\n\t\t\treturn (Object[]) Array.newInstance(clz, 0);\n\t\t}\n\t\treturn target.toArray((Object[]) Array.newInstance(clz, target.size()));\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/FactoryBean.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.util.Collection;\n\n/**\n * 这个类可以被exclude过滤，但是beans()返回的bean不受exclude过滤\n */\npublic interface FactoryBean {\n\t/**\n\t * 返回的列表中的元素，可以是class类、NamedBean或实例化后的对象。Collection、Map类型的Object，不会对它做额外处理。<BR>\n\t * 如果是实例化后的对象，该对象中sumk相关的注解(@Web、@Bean、@Soa等)会被忽略<BR>\n\t * 无论是对象还是class，这里的bean中的@Inject能被正确注入 注意：<B>不要出现类名重复的对象</B>\n\t * \n\t * @return 可以为null或空list\n\t */\n\tCollection<?> beans();\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/IOC.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.yx.base.Ordered;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\n\npublic final class IOC {\n\n\tprivate static BeanProvider provider = new InnerProvider();\n\n\tpublic static void setProvider(BeanProvider provider) {\n\t\tIOC.provider = Objects.requireNonNull(provider);\n\t}\n\n\t/**\n\t * 获取对应的bean\n\t * \n\t * @param <T>  返回值类型\n\t * @param name bean的名称\n\t * @return 如果不存在，就返回null。如果bean不止一个，会抛出SumkException异常\n\t */\n\tpublic static <T> T get(String name) {\n\t\treturn get(name, null);\n\t}\n\n\tpublic static <T> T get(Class<T> clz) {\n\t\treturn get(null, clz);\n\t}\n\n\tpublic static <T> T get(String name, Class<T> clz) {\n\t\treturn provider.getBean(name, clz);\n\t}\n\n\tpublic static <T> List<T> getBeans(Class<T> clz) {\n\t\treturn provider.getBeans(null, clz);\n\t}\n\n\t/**\n\t * 获取该类型bean的第一个，如果不存在就抛出异常。\n\t * \n\t * @param <T>        bean的类型\n\t * @param clz        bean的类型，返回order最小的那个\n\t * @param allowEmpty true表示允许为空，否则会抛出异常\n\t * @return 返回第一个符合条件的bean。被自定义名称的bean可能获取不到\n\t */\n\tpublic static <T extends Ordered> T getFirstBean(Class<T> clz, boolean allowEmpty) {\n\t\tList<T> factorys = getBeans(clz);\n\t\tif (factorys.isEmpty()) {\n\t\t\tif (allowEmpty) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tthrow new SumkException(353451, clz.getName() + \"没有实现类，或者实现类不是Bean\");\n\t\t}\n\t\tT f = factorys.get(0);\n\t\tLogs.ioc().debug(\"{}第一个bean是{}\", clz.getName(), f);\n\t\treturn f;\n\t}\n\n\tpublic static <T> List<T> getBeans(String name, Class<T> clz) {\n\t\treturn provider.getBeans(name, clz);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/InnerIOC.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.yx.bean.aop.AopExecutorChain;\nimport org.yx.bean.aop.AopExecutorManager;\n\npublic final class InnerIOC {\n\tprivate static final BeanPool pool = new BeanPool();\n\n\tstatic BeanPool pool() {\n\t\treturn pool;\n\t}\n\n\tpublic static <T> T putClass(String name, Class<T> clz) throws Exception {\n\t\treturn pool.putClass(name, clz);\n\t}\n\n\tpublic static <T> T putClassByInterface(Class<?> intf, Class<T> clz) throws Exception {\n\t\treturn pool.putClass(BeanKit.resloveBeanName(intf), clz);\n\t}\n\n\tpublic static <T> T putBean(String beanName, T bean) {\n\t\treturn pool.putBean(beanName, bean);\n\t}\n\n\tpublic static <T> T getOrCreate(Class<T> clz) throws Exception {\n\t\tT obj = IOC.get(clz);\n\t\tif (obj != null) {\n\t\t\treturn obj;\n\t\t}\n\t\treturn putClass(null, clz);\n\t}\n\n\tpublic static List<String> beanNames() {\n\t\treturn pool.beanNames();\n\t}\n\n\tpublic static NameSlot getSlot(String name) {\n\t\treturn pool.getSlot(name);\n\t}\n\n\tpublic static Collection<Object> beans() {\n\t\treturn pool.beans();\n\t}\n\n\tpublic static void clear() {\n\t\tpool.clear();\n\t}\n\n\tpublic static String info() {\n\t\treturn pool.toString();\n\t}\n\n\tpublic static AopExecutorChain getAopExecutorChain(int index) {\n\t\treturn AopExecutorManager.get().getChain(index);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/InnerProvider.java",
    "content": "package org.yx.bean;\n\nimport java.util.List;\n\npublic class InnerProvider implements BeanProvider {\n\n\t@Override\n\tpublic <T> List<T> getBeans(String name, Class<T> clz) {\n\t\treturn InnerIOC.pool().getBeans(name, clz);\n\t}\n\n\t@Override\n\tpublic List<String> beanNames() {\n\t\treturn InnerIOC.beanNames();\n\t}\n\n\t@Override\n\tpublic <T> T getBean(String name, Class<T> clz) {\n\t\treturn InnerIOC.pool().getBean(name, clz);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/InterfaceBean.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.util.Objects;\n\nimport org.yx.exception.SimpleSumkException;\n\npublic final class InterfaceBean implements ComplexBean {\n\tprivate final Class<?> intf;\n\n\tprivate final Object bean;\n\n\tpublic InterfaceBean(Class<?> intf, Object bean) {\n\t\tthis.intf = Objects.requireNonNull(intf);\n\t\tif (bean instanceof ComplexBean) {\n\t\t\tthrow new SimpleSumkException(233654645, \"bean can not be a ComplexBean object\");\n\t\t}\n\t\tthis.bean = Objects.requireNonNull(bean);\n\t}\n\n\tpublic Class<?> getIntf() {\n\t\treturn intf;\n\t}\n\n\tpublic Object getBean() {\n\t\treturn bean;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/NameSlot.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.yx.exception.SumkException;\nimport org.yx.exception.SumkExceptionCode;\nimport org.yx.log.Logs;\nimport org.yx.util.CollectionUtil;\n\npublic class NameSlot {\n\n\tprivate final String name;\n\tprivate Object[] beans;\n\n\tpublic NameSlot(String name, Object[] beans) {\n\t\tthis.name = name;\n\t\tthis.beans = beans == null ? new Object[0] : beans;\n\t}\n\n\tpublic List<Object> beans() {\n\t\treturn CollectionUtil.unmodifyList(beans);\n\t}\n\n\tpublic synchronized boolean appendBean(Object bean) {\n\t\tObject[] beans = this.beans;\n\t\tif (beans.length == 0) {\n\t\t\tthis.beans = new Object[] { bean };\n\t\t\treturn true;\n\t\t}\n\t\tClass<?> clz = BeanKit.getTargetClass(bean);\n\t\tfor (Object o : beans) {\n\t\t\tif (clz == BeanKit.getTargetClass(o)) {\n\t\t\t\tLogs.ioc().warn(\"{}={} duplicate,will be ignored.\", name, clz.getName());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tObject[] newBeans = new Object[beans.length + 1];\n\t\tSystem.arraycopy(beans, 0, newBeans, 0, beans.length);\n\t\tnewBeans[newBeans.length - 1] = bean;\n\t\tthis.beans = newBeans;\n\t\treturn true;\n\t}\n\n\tprivate Class<?> resolveType(Class<?> clz) {\n\t\tif (clz == null) {\n\t\t\treturn Object.class;\n\t\t}\n\t\tif (Boxed.class.isAssignableFrom(clz)) {\n\t\t\treturn clz.getSuperclass();\n\t\t}\n\t\treturn clz;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> T getBean(Class<T> clz) {\n\t\tclz = (Class<T>) this.resolveType(clz);\n\n\t\tList<Object> ret = new ArrayList<>(2);\n\t\tfor (Object b : this.beans) {\n\t\t\tClass<?> targetClz = BeanKit.getTargetClass(b);\n\t\t\tif (targetClz == clz) {\n\t\t\t\treturn (T) b;\n\t\t\t}\n\t\t\tif (clz.isAssignableFrom(targetClz)) {\n\t\t\t\tret.add(b);\n\t\t\t}\n\t\t}\n\t\tif (ret.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (ret.size() > 1) {\n\t\t\tthrow new SumkException(SumkExceptionCode.TOO_MANY_BEAN, name + \"存在多个\" + clz.getName() + \"实例\");\n\t\t}\n\t\treturn (T) ret.get(0);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> List<T> getBeans(Class<T> clz) {\n\t\tclz = (Class<T>) this.resolveType(clz);\n\t\tList<T> list = new ArrayList<>(this.beans.length);\n\n\t\tfor (Object o : this.beans) {\n\t\t\tif (clz.isInstance(o)) {\n\t\t\t\tlist.add((T) o);\n\t\t\t}\n\t\t}\n\t\tif (list.size() > 1 && Comparable.class.isAssignableFrom(clz)) {\n\t\t\tlist.sort(null);\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic String name() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder().append(name).append(\" :\");\n\t\tfor (Object o : this.beans) {\n\t\t\tsb.append(\"  \").append(BeanKit.getTargetClass(o).getName());\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/NamedBean.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.util.Objects;\n\nimport org.yx.exception.SimpleSumkException;\n\npublic final class NamedBean implements ComplexBean {\n\tprivate final String beanName;\n\t/**\n\t * 可以是class类，也可以包含实力化后的对象。但不能是NamedBean\n\t */\n\tprivate final Object bean;\n\n\t/**\n\t * @param beanName bean的名称，不能为null或空串\n\t * @param bean     可以是class类，也可以包含实力化后的对象。但不能是NamedBean\n\t */\n\tpublic NamedBean(String beanName, Object bean) {\n\t\tbeanName = Objects.requireNonNull(beanName, \"beanName cannot be null\").trim();\n\t\tif (beanName.isEmpty()) {\n\t\t\tthrow new SimpleSumkException(233654645, \"bean name can not be empty\");\n\t\t}\n\t\tif (bean instanceof ComplexBean) {\n\t\t\tthrow new SimpleSumkException(233654645, \"bean can not be a ComplexBean object\");\n\t\t}\n\t\tthis.beanName = beanName;\n\t\tthis.bean = Objects.requireNonNull(bean);\n\t}\n\n\tpublic String getBeanName() {\n\t\treturn beanName;\n\t}\n\n\tpublic Object getBean() {\n\t\treturn bean;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/ParallelBootWatcher.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.function.Predicate;\n\nimport org.yx.bean.watcher.BootWatcher;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\nimport org.yx.main.SumkServer;\n\npublic abstract class ParallelBootWatcher implements BootWatcher {\n\n\t/**\n\t * ioc启动处理\n\t * \n\t * @param clz 这个clz是原始的类\n\t * @throws Exception 处理时发生的异常\n\t */\n\tprotected abstract void handle(Class<?> clz) throws Exception;\n\n\t@Override\n\tpublic List<Class<?>> publish(List<Class<?>> sortedClasses, Predicate<String> optional) throws Exception {\n\t\tExecutorService executor = createExecutor();\n\t\ttry {\n\t\t\tparallelPublish(sortedClasses, optional, executor);\n\t\t} finally {\n\t\t\texecutor.shutdown();\n\t\t}\n\t\treturn null;\n\t}\n\n\tprotected void serialPublish(List<Class<?>> sortedClasses, Predicate<String> optional) throws Exception {\n\t\tfor (Class<?> clz : sortedClasses) {\n\t\t\tBootCallable c = new BootCallable(clz, optional, this);\n\t\t\tc.call();\n\t\t}\n\t}\n\n\tprotected void parallelPublish(List<Class<?>> scanedClasses, Predicate<String> optional, ExecutorService executor)\n\t\t\tthrows InterruptedException, ExecutionException, TimeoutException {\n\t\tList<Future<Boolean>> futures = new ArrayList<>(scanedClasses.size());\n\t\tfor (Class<?> clz : scanedClasses) {\n\t\t\tBootCallable c = new BootCallable(clz, optional, this);\n\t\t\tFuture<Boolean> f = executor.submit(c);\n\t\t\tfutures.add(f);\n\t\t}\n\t\tfor (Future<Boolean> f : futures) {\n\t\t\tf.get(SumkServer.startTimeout(), TimeUnit.MILLISECONDS);\n\t\t}\n\t}\n\n\tprotected ExecutorService createExecutor() {\n\t\tint poolSize = AppInfo.getInt(\"sumk.ioc.booter.threads\", 4);\n\t\tif (poolSize < 1) {\n\t\t\tpoolSize = 1;\n\t\t}\n\t\tThreadPoolExecutor excutor = new ThreadPoolExecutor(poolSize, poolSize, 10L, TimeUnit.SECONDS,\n\t\t\t\tnew LinkedBlockingQueue<Runnable>());\n\t\texcutor.allowCoreThreadTimeOut(true);\n\t\treturn excutor;\n\t}\n\n\tprivate static class BootCallable implements Callable<Boolean> {\n\n\t\tprivate final Class<?> clz;\n\t\tprivate final Predicate<String> optional;\n\t\tprivate final ParallelBootWatcher watcher;\n\n\t\tpublic BootCallable(Class<?> clz, Predicate<String> optional, ParallelBootWatcher watcher) {\n\t\t\tthis.clz = clz;\n\t\t\tthis.optional = optional;\n\t\t\tthis.watcher = watcher;\n\t\t}\n\n\t\t@Override\n\t\tpublic Boolean call() throws Exception {\n\t\t\ttry {\n\t\t\t\twatcher.handle(clz);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tString c = clz.getName();\n\n\t\t\t\tif ((e instanceof LinkageError || e instanceof ClassNotFoundException)\n\t\t\t\t\t\t&& (c.startsWith(\"org.yx.\") || optional.test(c))) {\n\n\t\t\t\t\tLogs.ioc().debug(\"{} ignored in {} publish because: [{}]\", c, watcher.getClass().getName(), e);\n\t\t\t\t\treturn Boolean.TRUE;\n\t\t\t\t}\n\t\t\t\tLogs.ioc().error(c + \" 在 \" + watcher.getClass().getName() + \"中执行失败\", e);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t\treturn Boolean.TRUE;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/Plugin.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport org.yx.base.Ordered;\n\n/**\n * 实现该接口，并且添加@Bean注解。会在sumk启动或停止的时候，被调用<BR>\n * 这个接口一般用于mongo初始化等。但不用于修改IOC的内部结构。\n * \n * @author 游夏\n *\n */\npublic interface Plugin extends Ordered {\n\t/**\n\t * 在startAsync()之前执行，同步执行。如果抛出异常，会导致应用启动失败\n\t */\n\tdefault void prepare() {\n\n\t}\n\n\t/**\n\t * 在所有的prepare()之后异步执行。 如果抛出异常，会导致应用启动失败\n\t */\n\tvoid startAsync();\n\n\t/**\n\t * 在所有的startAsync()之后执行。如果抛出异常，会导致应用启动失败\n\t */\n\tdefault void afterStarted() {\n\n\t}\n\n\tdefault void stop() {\n\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/PluginBooter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean;\n\nimport java.util.List;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.yx.base.context.AppContext;\nimport org.yx.base.thread.SumkExecutorService;\nimport org.yx.common.validate.Validators;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\nimport org.yx.main.SumkServer;\nimport org.yx.util.SumkThreadPool;\n\npublic class PluginBooter {\n\n\tpublic void start() {\n\t\tValidators.init();\n\t\tstartBeans();\n\t}\n\n\tprivate void startBeans() {\n\t\tList<Plugin> plugins = IOC.getBeans(Plugin.class);\n\t\tif (plugins == null || plugins.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (Plugin plugin : plugins) {\n\t\t\tplugin.prepare();\n\t\t}\n\t\tCountDownLatch latch = new CountDownLatch(plugins.size());\n\t\tSumkExecutorService executor = SumkThreadPool.executor();\n\t\tfor (Plugin plugin : plugins) {\n\t\t\texecutor.execute(() -> {\n\t\t\t\ttry {\n\t\t\t\t\tplugin.startAsync();\n\t\t\t\t\tlatch.countDown();\n\t\t\t\t\tLogs.ioc().debug(\"{} startAsync finished\", plugin.getClass().getSimpleName());\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\tLogs.ioc().error(plugin.getClass().getSimpleName() + \" start failed\", e);\n\t\t\t\t\tAppContext.startFailed();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tpreHotCoreThreads(executor);\n\t\tlong timeout = SumkServer.startTimeout();\n\t\ttry {\n\t\t\tif (!latch.await(timeout, TimeUnit.MILLISECONDS)) {\n\t\t\t\tLogs.ioc().error(\"plugin无法在{}ms内全部启动，导致整个项目启动超时\", timeout);\n\t\t\t\tAppContext.startFailed();\n\t\t\t}\n\t\t} catch (InterruptedException e) {\n\t\t\tLogs.ioc().error(\"receive InterruptedException in plugin starting\");\n\t\t\tThread.currentThread().interrupt();\n\t\t\tAppContext.startFailed();\n\t\t}\n\t\tfor (Plugin plugin : plugins) {\n\t\t\tplugin.afterStarted();\n\t\t}\n\t\tLogs.ioc().debug(\"all plugin started\");\n\t}\n\n\tprivate void preHotCoreThreads(SumkExecutorService executor) {\n\t\tif (SumkServer.isRpcEnable() || SumkServer.isHttpEnable()) {\n\t\t\tSumkThreadPool.executor().setCorePoolSize(200);\n\t\t}\n\t\tif (AppInfo.getBoolean(\"sumk.thread.prestartAllCoreThreads\", true)\n\t\t\t\t&& (SumkServer.isHttpEnable() || SumkServer.isRpcEnable()) && executor instanceof ThreadPoolExecutor) {\n\t\t\tThreadPoolExecutor pool = (ThreadPoolExecutor) executor;\n\t\t\tpool.prestartAllCoreThreads();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/AopContext.java",
    "content": "package org.yx.bean.aop;\n\nimport java.util.Objects;\n\npublic class AopContext {\n\tprivate final AopExecutorSupplier supplier;\n\tprivate final Object attach;\n\n\tpublic AopContext(AopExecutorSupplier supplier, Object attach) {\n\t\tthis.supplier = Objects.requireNonNull(supplier);\n\t\tthis.attach = attach;\n\t}\n\n\tpublic AopExecutor getAopExecutor() {\n\t\treturn this.supplier.get(attach);\n\t}\n\n\tpublic AopExecutorSupplier getSupplier() {\n\t\treturn supplier;\n\t}\n\n\tpublic Object getAttach() {\n\t\treturn attach;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(attach, supplier);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tAopContext other = (AopContext) obj;\n\t\treturn Objects.equals(attach, other.attach) && Objects.equals(supplier, other.supplier);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/AopExecutor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop;\n\n/**\n * <B>返回值如果是原始类型，因为不支持null，所以在before()返回false和after()吃掉异常的情况下，框架会抛出异常</B>。 执行顺序：\n * <OL>\n * <LI>先执行before()，如果返回false，就直接执行close()</LI>\n * <LI>如果before()和业务方法都执行成功，就执行after()</LI>\n * <LI>有异常发生就执行onError(),这个是用来转换异常，如果异常被前面的Executor转换了，将不再被执行。这个方法不建议抛出异常</LI>\n * <LI>close()无论什么情况都会被执行。这个方法不建议抛出异常</LI>\n * </OL>\n * \n * @author youtl\n *\n */\npublic interface AopExecutor {\n\n\t/**\n\t * 这个方法可以抛出异常，如果抛出异常，就不会执行后面的AopExecutor，并且真正的业务方法也不会被执行你\n\t * \n\t * @param params 原始方法的参数\n\t * @throws Exception 如果抛出异常，会中断后面的执行\n\t */\n\tvoid before(Object[] params) throws Exception;\n\n\t/**\n\t * 无论上面的before()方法执行得怎么样，所有调用过before()的AopExecutor， 它的close()都会确保被调用到\n\t * \n\t * @param result         原始方法的返回值。返回值引用不可变，但可以修改返回值的内部属性\n\t * @param e              原始或上一个AopExecutor的异常，如果没有发生异常，它就是null\n\t * @param methodExecuted true表示业务方法有被执行\n\t * @return 原始异常或者处理后的异常。如果返回null,就表示没有异常了\n\t */\n\tThrowable after(Object result, Throwable e, boolean methodExecuted);\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/AopExecutorChain.java",
    "content": "package org.yx.bean.aop;\n\nimport org.yx.log.Logs;\nimport org.yx.util.ExceptionUtil;\n\n/**\n * 嵌套式执行，先执行before()的晚执行after()\n * \n * @author youtl\n *\n */\npublic class AopExecutorChain {\n\tprivate final AopExecutor[] executors;\n\tprivate int lastExecutor;\n\n\tpublic AopExecutorChain(AopExecutor[] excutors) {\n\t\tthis.executors = excutors;\n\t}\n\n\tpublic void before(Object[] params) throws Exception {\n\t\tfor (int i = 0; i < executors.length; i++) {\n\t\t\tthis.lastExecutor = i;\n\t\t\tAopExecutor excutor = executors[lastExecutor];\n\t\t\texcutor.before(params);\n\t\t}\n\t}\n\n\tpublic void after(Object result, Throwable e, boolean methodExecuted) {\n\t\tThrowable ex = e;\n\t\tfor (int i = lastExecutor; i >= 0; i--) {\n\t\t\ttry {\n\t\t\t\tex = executors[i].after(result, ex, methodExecuted);\n\t\t\t} catch (Throwable e2) {\n\t\t\t\tLogs.aop().error(\"excpetion raised when after() in aopExecutor \" + executors[i], e2);\n\t\t\t\tex = e2;\n\t\t\t}\n\t\t}\n\t\tif (ex != null) {\n\t\t\tLogs.aop().error(ex.getMessage(), ex);\n\t\t\tthrow ExceptionUtil.toRuntimeException(ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/AopExecutorManager.java",
    "content": "package org.yx.bean.aop;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.yx.bean.IOC;\n\npublic class AopExecutorManager {\n\n\tprivate static AopExecutorManager INSTANCE = new AopExecutorManager();\n\n\tpublic static AopExecutorManager get() {\n\t\treturn INSTANCE;\n\t}\n\n\tpublic static void reset() {\n\t\tINSTANCE = new AopExecutorManager();\n\t}\n\n\tprivate List<AopContext[]> aopContexts = new ArrayList<>();\n\n\tpublic AopExecutorChain getChain(int index) {\n\t\tAopContext[] aopContexts = this.aopContexts.get(index);\n\t\tAopExecutor[] excutors = new AopExecutor[aopContexts.length];\n\t\tfor (int i = 0; i < aopContexts.length; i++) {\n\t\t\texcutors[i] = aopContexts[i].getAopExecutor();\n\t\t}\n\t\treturn new AopExecutorChain(excutors);\n\t}\n\n\tpublic List<AopContext> willProxyExcutorSuppliers(Class<?> clz, Method method) {\n\t\tList<AopExecutorSupplier> baseSuppliers = IOC.getBeans(AopExecutorSupplier.class);\n\t\tif (baseSuppliers.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<AopContext> list = new ArrayList<>(baseSuppliers.size());\n\t\tfor (AopExecutorSupplier supplier : baseSuppliers) {\n\t\t\tObject attach = supplier.willProxy(clz, method);\n\t\t\tif (attach != null) {\n\t\t\t\tlist.add(new AopContext(supplier, attach));\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tpublic synchronized int indexSupplier(List<AopContext> supplierList) {\n\t\tAopContext[] supplier = supplierList.toArray(new AopContext[supplierList.size()]);\n\t\tint currentLength = this.aopContexts.size();\n\t\tthis.aopContexts.add(supplier);\n\t\treturn currentLength;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/AopExecutorSupplier.java",
    "content": "package org.yx.bean.aop;\n\nimport java.lang.reflect.Method;\n\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.base.Ordered;\n\n/**\n * <UL>\n * <LI>实现类也需要@Bean注解，但是它的willProxy()不能调用其它的bean</LI>\n * <LI>执行顺序由order()方法决定。</LI>\n * </UL>\n *\n */\npublic interface AopExecutorSupplier extends Ordered {\n\n\t/**\n\t * 判断是否满足代理条件，本方法只在IOC框架启动的时候调用，并且在bean初始化之前调用，所以里面不能使用其它的bean。\n\t * 私有方法等不符合代理条件的方法，不会调用这个方法\n\t * \n\t * @param clz       方法的原始类\n\t * @param rawMethod 原始的方法\n\t * @return null表示不需要代理，其它对象表示需要代理，最简单的办法就是返回rawMethod。该返回的对象，会传递给get()方法\n\t */\n\tObject willProxy(Class<?> clz, Method rawMethod);\n\n\t/**\n\t * 一次aop方法执行，就会调用一次每个符合当前aop条件的AopExcutorSupplier。 返回的对象可以每次都不同，也可以是同一个。<BR>\n\t * <B>注意本方法不能抛出异常，也不要开启资源占用，占用资源应该在before()里做</B>\n\t * \n\t * @param attach willProxy方法返回的对象\n\t * @return aop的执行器，不能为null\n\t */\n\t@NotNull\n\tAopExecutor get(Object attach);\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/AsmUtils.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.util.Loader;\n\npublic final class AsmUtils {\n\tprivate static final Map<ClassLoader, ProxyClassLoader> classLoaders = new ConcurrentHashMap<>();\n\n\tpublic static String proxyCalssName(Class<?> clz) {\n\t\tString name = clz.getName();\n\t\tint index = name.lastIndexOf('.');\n\t\treturn name.substring(0, index) + \".sumkbox\" + name.substring(index);\n\t}\n\n\tpublic static int asmVersion() {\n\t\treturn AppInfo.getInt(\"sumk.asm.version\", Opcodes.ASM7);\n\t}\n\n\tpublic static int jvmVersion() {\n\t\treturn AppInfo.getInt(\"sumk.asm.jvm.version\", Opcodes.V1_8);\n\t}\n\n\tpublic static InputStream openStreamForClass(Class<?> clz) {\n\t\tString internalName = clz.getName().replace('.', '/') + \".class\";\n\t\treturn Loader.getResourceAsStream(internalName);\n\t}\n\n\tpublic static boolean sameType(Type[] types, Class<?>[] clazzes) {\n\n\t\tif (types.length != clazzes.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (int i = 0; i < types.length; i++) {\n\t\t\tif (!Type.getType(clazzes[i]).equals(types[i])) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static List<MethodParamInfo> buildMethodInfos(List<Method> methods) throws IOException {\n\t\tMap<Class<?>, List<Method>> map = new HashMap<>();\n\t\tfor (Method m : methods) {\n\t\t\tList<Method> list = map.get(m.getDeclaringClass());\n\t\t\tif (list == null) {\n\t\t\t\tlist = new ArrayList<>();\n\t\t\t\tmap.put(m.getDeclaringClass(), list);\n\t\t\t}\n\t\t\tlist.add(m);\n\t\t}\n\t\tList<MethodParamInfo> ret = new ArrayList<>();\n\t\tfor (List<Method> ms : map.values()) {\n\t\t\tret.addAll(buildMethodInfos(ms.get(0).getDeclaringClass(), ms));\n\t\t}\n\t\treturn ret;\n\t}\n\n\tprivate static List<MethodParamInfo> buildMethodInfos(Class<?> declaringClass, List<Method> methods)\n\t\t\tthrows IOException {\n\t\tClassReader cr = new ClassReader(openStreamForClass(declaringClass));\n\t\tMethodInfoClassVisitor cv = new MethodInfoClassVisitor(methods);\n\t\tcr.accept(cv, 0);\n\t\treturn cv.getMethodInfos();\n\t}\n\n\tpublic static Method getMethod(Class<?> clz, String methodName, Class<?>[] paramTypes) {\n\t\twhile (clz != Object.class) {\n\t\t\tMethod[] ms = clz.getDeclaredMethods();\n\t\t\tfor (Method m : ms) {\n\t\t\t\tif (!m.getName().equals(methodName)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tClass<?>[] paramTypes2 = m.getParameterTypes();\n\t\t\t\tif (!Arrays.equals(paramTypes2, paramTypes)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\treturn m;\n\t\t\t}\n\t\t\tclz = clz.getSuperclass();\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static ProxyClassLoader getProxyClassLoader(ClassLoader originClassLoader) {\n\t\tif (originClassLoader == null) {\n\t\t\toriginClassLoader = Loader.loader();\n\t\t}\n\t\tif (originClassLoader instanceof ProxyClassLoader) {\n\t\t\treturn (ProxyClassLoader) originClassLoader;\n\t\t}\n\t\treturn classLoaders.computeIfAbsent(originClassLoader, k -> new ProxyClassLoader(k));\n\t}\n\n\tpublic static Class<?> defineClass(String fullName, byte[] b, ClassLoader originClassLoader) throws Exception {\n\n\t\tString clzOutPath = AppInfo.get(\"sumk.asm.debug.output\");\n\t\tif (clzOutPath != null && clzOutPath.length() > 0) {\n\t\t\ttry {\n\t\t\t\tFile f = new File(clzOutPath, fullName + \".class\");\n\t\t\t\ttry (FileOutputStream fos = new FileOutputStream(f)) {\n\t\t\t\t\tfos.write(b);\n\t\t\t\t\tfos.flush();\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tif (Logs.asm().isTraceEnabled()) {\n\t\t\t\t\tLogs.asm().error(e.getLocalizedMessage(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tProxyClassLoader classLoader = getProxyClassLoader(originClassLoader);\n\t\tsynchronized (AsmUtils.class) {\n\t\t\ttry {\n\t\t\t\tClass<?> clz = classLoader.loadClass(fullName);\n\t\t\t\tLogs.asm().debug(\"{} was defined\", fullName);\n\t\t\t\treturn clz;\n\t\t\t} catch (Throwable e) {\n\t\t\t\tif (!(e instanceof ClassNotFoundException)) {\n\t\t\t\t\tLogs.asm().warn(fullName + \" 加载失败\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t\tClass<?> clz = classLoader.defineClass(fullName, b);\n\t\t\tif (clz == null) {\n\t\t\t\tthrow new SumkException(235345436, \"cannot load class \" + fullName);\n\t\t\t}\n\t\t\treturn clz;\n\t\t}\n\t}\n\n\tpublic static final int BADMODIFIERS = Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL | Modifier.PRIVATE;\n\n\tpublic static boolean notPublicOnly(int modifiers) {\n\n\t\treturn (modifiers & (Modifier.PUBLIC | BADMODIFIERS)) != Modifier.PUBLIC;\n\t}\n\n\tpublic static boolean canProxy(int modifiers) {\n\t\treturn (modifiers & BADMODIFIERS) == 0;\n\t}\n\n\tpublic static List<Object> getImplicitFrame(String desc) {\n\t\tList<Object> locals = new ArrayList<>(5);\n\t\tif (desc.isEmpty()) {\n\t\t\treturn locals;\n\t\t}\n\t\tint i = 0;\n\t\twhile (desc.length() > i) {\n\t\t\tint j = i;\n\t\t\tswitch (desc.charAt(i++)) {\n\t\t\tcase 'Z':\n\t\t\tcase 'C':\n\t\t\tcase 'B':\n\t\t\tcase 'S':\n\t\t\tcase 'I':\n\t\t\t\tlocals.add(Opcodes.INTEGER);\n\t\t\t\tbreak;\n\t\t\tcase 'F':\n\t\t\t\tlocals.add(Opcodes.FLOAT);\n\t\t\t\tbreak;\n\t\t\tcase 'J':\n\t\t\t\tlocals.add(Opcodes.LONG);\n\t\t\t\tbreak;\n\t\t\tcase 'D':\n\t\t\t\tlocals.add(Opcodes.DOUBLE);\n\t\t\t\tbreak;\n\t\t\tcase '[':\n\t\t\t\twhile (desc.charAt(i) == '[') {\n\t\t\t\t\t++i;\n\t\t\t\t}\n\t\t\t\tif (desc.charAt(i) == 'L') {\n\t\t\t\t\t++i;\n\t\t\t\t\twhile (desc.charAt(i) != ';') {\n\t\t\t\t\t\t++i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlocals.add(desc.substring(j, ++i));\n\t\t\t\tbreak;\n\t\t\tcase 'L':\n\t\t\t\twhile (desc.charAt(i) != ';') {\n\t\t\t\t\t++i;\n\t\t\t\t}\n\t\t\t\tlocals.add(desc.substring(j + 1, i++));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn locals;\n\t}\n\n\tpublic static Method getSameMethod(Method method, Class<?> otherClass) {\n\t\tClass<?> clz = method.getDeclaringClass();\n\t\tif (clz == otherClass) {\n\t\t\treturn method;\n\t\t}\n\t\tString methodName = method.getName();\n\t\tClass<?>[] argTypes = method.getParameterTypes();\n\n\t\tMethod[] proxyedMethods = otherClass.getMethods();\n\t\tfor (Method proxyedMethod : proxyedMethods) {\n\t\t\tif (proxyedMethod.getName().equals(methodName) && Arrays.equals(argTypes, proxyedMethod.getParameterTypes())\n\t\t\t\t\t&& !proxyedMethod.getDeclaringClass().isInterface()) {\n\t\t\t\treturn proxyedMethod;\n\t\t\t}\n\t\t}\n\t\treturn method;\n\t}\n\n\tpublic static void clearProxyClassLoaders() {\n\t\tclassLoaders.clear();\n\t}\n\n\tpublic static HashMap<ClassLoader, ProxyClassLoader> aopClassLoaders() {\n\t\treturn new HashMap<>(classLoaders);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/MethodInfoClassVisitor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.objectweb.asm.AnnotationVisitor;\nimport org.objectweb.asm.Attribute;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.ModuleVisitor;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.TypePath;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.util.StringUtil;\n\nclass MethodInfoClassVisitor extends ClassVisitor {\n\n\tprivate final List<MethodParamInfo> infos;\n\n\tpublic MethodInfoClassVisitor(List<Method> methods) {\n\t\tsuper(AsmUtils.asmVersion());\n\t\tthis.infos = new ArrayList<>(Objects.requireNonNull(methods).size());\n\t\tfor (Method method : methods) {\n\t\t\tinfos.add(createMethodInfo(method));\n\t\t}\n\n\t}\n\n\tprivate MethodParamInfo createMethodInfo(Method m) {\n\t\tType[] tys = Type.getArgumentTypes(m);\n\n\t\tString[] descriptor = new String[tys.length];\n\t\tfor (int i = 0; i < tys.length; i++) {\n\t\t\tdescriptor[i] = tys[i].getDescriptor();\n\t\t}\n\t\treturn new MethodParamInfo(m, new String[tys.length], descriptor, new String[tys.length]);\n\t}\n\n\tprivate MethodParamInfo getMethodParamInfo(String methodName, String desc) {\n\t\tfor (MethodParamInfo mp : this.infos) {\n\t\t\tif (mp.isSameMethod(methodName, desc)) {\n\t\t\t\treturn mp;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic MethodVisitor visitMethod(final int access, final String methodName, final String desc,\n\t\t\tfinal String signature, final String[] exceptions) {\n\n\t\tMethodParamInfo info = this.getMethodParamInfo(methodName, desc);\n\t\tif (info == null || info.getArgNames().length == 0) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new ParseParamsMethodVisitor(AsmUtils.asmVersion(), info);\n\t}\n\n\t@Override\n\tpublic void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n\t}\n\n\t@Override\n\tpublic void visitSource(String source, String debug) {\n\t}\n\n\t@Override\n\tpublic ModuleVisitor visitModule(String name, int access, String version) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void visitNestHost(String nestHost) {\n\t}\n\n\t@Override\n\tpublic void visitOuterClass(String owner, String name, String descriptor) {\n\t}\n\n\t@Override\n\tpublic AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void visitAttribute(Attribute attribute) {\n\t}\n\n\t@Override\n\tpublic void visitNestMember(String nestMember) {\n\t}\n\n\t@Override\n\tpublic void visitInnerClass(String name, String outerName, String innerName, int access) {\n\t}\n\n\t@Override\n\tpublic FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void visitEnd() {\n\t}\n\n\tpublic List<MethodParamInfo> getMethodInfos() {\n\t\tfor (MethodParamInfo info : this.infos) {\n\t\t\tfor (String name : info.getArgNames()) {\n\t\t\t\tif (StringUtil.isEmpty(name)) {\n\t\t\t\t\tLogs.asm().error(\"{}.{}() has empty name。params:{}\", info.getMethod().getClass().getSimpleName(),\n\t\t\t\t\t\t\tinfo.getMethod().getName(), Arrays.toString(info.getArgNames()));\n\t\t\t\t\tthrow new SumkException(362655465, \"params not full parsed\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn this.infos;\n\t}\n}"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/MethodParamInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\nimport java.lang.reflect.Method;\n\nimport org.objectweb.asm.Type;\n\npublic final class MethodParamInfo {\n\n\tprivate final Method method;\n\tprivate String[] argNames;\n\tprivate String[] descs;\n\tprivate String[] signatures;\n\tprivate final String methodDesc;\n\n\tpublic String[] getArgNames() {\n\t\treturn argNames;\n\t}\n\n\tpublic String[] getDescs() {\n\t\treturn descs;\n\t}\n\n\tpublic String[] getSignatures() {\n\t\treturn signatures;\n\t}\n\n\tpublic MethodParamInfo(Method method, String[] argNames, String[] descs, String[] signatures) {\n\t\tthis.method = method;\n\t\tthis.argNames = argNames;\n\t\tthis.descs = descs;\n\t\tthis.signatures = signatures;\n\t\tthis.methodDesc = Type.getMethodDescriptor(method);\n\t}\n\n\tpublic Method getMethod() {\n\t\treturn method;\n\t}\n\n\tpublic String getMethodDesc() {\n\t\treturn methodDesc;\n\t}\n\n\tpublic boolean isSameMethod(String methodName, String desc) {\n\n\t\treturn methodName.equals(method.getName()) && this.methodDesc.equals(desc)\n\t\t\t\t&& AsmUtils.sameType(Type.getArgumentTypes(desc), method.getParameterTypes());\n\t}\n\n\tpublic Type[] getArgumentTypes() {\n\t\treturn Type.getArgumentTypes(this.methodDesc);\n\t}\n\n\tpublic Class<?> getDeclaringClass() {\n\t\treturn this.method.getDeclaringClass();\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/MethodPojo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\nimport java.lang.reflect.Type;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.Loader;\n\npublic class MethodPojo {\n\tprivate final Type[] paramTypes;\n\tprivate final String[] paramNames;\n\tprivate final Class<? extends ParamPojo> paramClz;\n\tprivate final ParamPojo sample;\n\n\tpublic MethodPojo(Class<? extends ParamPojo> clz, String[] names, Type[] fieldsType) {\n\t\tthis.paramTypes = Objects.requireNonNull(fieldsType);\n\t\tthis.paramNames = Objects.requireNonNull(names);\n\t\tthis.paramClz = Objects.requireNonNull(clz);\n\t\ttry {\n\t\t\tthis.sample = Loader.newInstance(this.paramClz);\n\t\t} catch (Exception e) {\n\t\t\tLogs.asm().error(clz.getName() + \"初始化失败\", e);\n\t\t\tthrow new SumkException(345354334, clz.getSimpleName() + \"初始化失败\");\n\t\t}\n\t}\n\n\tpublic List<Type> paramTypes() {\n\t\treturn CollectionUtil.unmodifyList(paramTypes);\n\t}\n\n\tpublic List<String> paramNames() {\n\t\treturn CollectionUtil.unmodifyList(paramNames);\n\t}\n\n\tpublic Class<? extends ParamPojo> paramClz() {\n\t\treturn paramClz;\n\t}\n\n\tpublic int getIndex(String name) {\n\t\tfor (int i = 0; i < paramNames.length; i++) {\n\t\t\tif (paramNames[i].equals(name)) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic Type getParamType(int index) {\n\t\treturn this.paramTypes[index];\n\t}\n\n\tpublic String getParamName(int index) {\n\t\treturn this.paramNames[index];\n\t}\n\n\tpublic ParamPojo createParamPojo(Map<String, Object> map) throws Exception {\n\t\tObject[] objs = new Object[paramNames.length];\n\t\tfor (int i = 0; i < objs.length; i++) {\n\t\t\tobjs[i] = map.get(paramNames[i]);\n\t\t}\n\t\tParamPojo pojo = createEmptyParamObj();\n\t\tpojo.setParams(objs);\n\t\treturn pojo;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends ParamPojo> T createEmptyParamObj() {\n\t\treturn (T) sample.createEmpty();\n\t}\n\n\tpublic int paramLength() {\n\t\treturn this.paramNames.length;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/ParamPojo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\npublic interface ParamPojo {\n\n\tObject[] params();\n\n\tObject invoke(Object owner) throws Throwable;\n\n\tvoid setParams(Object[] objs);\n\n\tParamPojo createEmpty();\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/ParamPojoClassFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\nimport static org.objectweb.asm.Opcodes.AALOAD;\nimport static org.objectweb.asm.Opcodes.ACC_PUBLIC;\nimport static org.objectweb.asm.Opcodes.ACC_SUPER;\nimport static org.objectweb.asm.Opcodes.ALOAD;\nimport static org.objectweb.asm.Opcodes.ARETURN;\nimport static org.objectweb.asm.Opcodes.CHECKCAST;\nimport static org.objectweb.asm.Opcodes.DUP;\nimport static org.objectweb.asm.Opcodes.GETFIELD;\nimport static org.objectweb.asm.Opcodes.IFNULL;\nimport static org.objectweb.asm.Opcodes.INVOKESPECIAL;\nimport static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;\nimport static org.objectweb.asm.Opcodes.NEW;\nimport static org.objectweb.asm.Opcodes.PUTFIELD;\nimport static org.objectweb.asm.Opcodes.RETURN;\n\nimport java.lang.reflect.Method;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.objectweb.asm.ClassWriter;\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\n\nfinal class ParamPojoClassFactory {\n\tprivate static final AtomicInteger SEED = new AtomicInteger(0);\n\n\tfinal MethodParamInfo p;\n\tfinal Method method;\n\n\tfinal String fullName;\n\tprivate ClassWriter cw;\n\tprivate Arg[] args;\n\n\tpublic ParamPojoClassFactory(MethodParamInfo p) {\n\t\tthis.p = p;\n\t\tmethod = p.getMethod();\n\t\tfullName = \"org/ytl/pojo/\" + String.join(\"_\", p.getDeclaringClass().getSimpleName(), method.getName(),\n\t\t\t\t\"\" + SEED.incrementAndGet());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic Class<? extends ParamPojo> create() throws Exception {\n\t\tif (Logs.asm().isTraceEnabled()) {\n\t\t\tLogs.asm().trace(\"begin generate paramters pojo :{}\", fullName);\n\t\t}\n\n\t\tcw = new ClassWriter(AppInfo.getInt(\"sumk.asm.writer.parampojo.compute\", ClassWriter.COMPUTE_MAXS));\n\t\tcw.visit(AsmUtils.jvmVersion(), ACC_PUBLIC | ACC_SUPER, fullName, null, \"java/lang/Object\",\n\t\t\t\tnew String[] { \"org/yx/bean/aop/asm/ParamPojo\" });\n\t\tthis.args = new Arg[p.getArgNames().length];\n\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\tString arg = p.getArgNames()[i];\n\t\t\tString desc = p.getDescs()[i];\n\t\t\targs[i] = new Arg(arg, desc);\n\t\t\tcw.visitField(ACC_PUBLIC, arg, desc, p.getSignatures()[i], null).visitEnd();\n\t\t}\n\n\t\tthis.buildInit();\n\t\tthis.buildParams();\n\t\tthis.buildInvoke();\n\t\tthis.buildCreateEmpty();\n\t\tthis.buildSetParams();\n\n\t\tcw.visitEnd();\n\n\t\treturn (Class<? extends ParamPojo>) AsmUtils.defineClass(fullName.replace('/', '.'), cw.toByteArray(),\n\t\t\t\tp.getDeclaringClass().getClassLoader());\n\t}\n\n\tprivate void buildInit() {\n\t\tMethodVisitor mv = cw.visitMethod(ACC_PUBLIC, \"<init>\", \"()V\", null, null);\n\t\tmv.visitCode();\n\t\tmv.visitVarInsn(ALOAD, 0);\n\t\tmv.visitMethodInsn(INVOKESPECIAL, \"java/lang/Object\", \"<init>\", \"()V\", false);\n\t\tmv.visitInsn(RETURN);\n\t\tmv.visitMaxs(1, 1);\n\t\tmv.visitEnd();\n\t}\n\n\tprivate void buildParams() {\n\t\tMethodVisitor mv = cw.visitMethod(ACC_PUBLIC, \"params\", \"()[Ljava/lang/Object;\", null, null);\n\t\tmv.visitCode();\n\t\tbuildArgArray(fullName, mv, args, method.getParameterTypes());\n\t\tmv.visitInsn(ARETURN);\n\t\tmv.visitMaxs(1, 0);\n\t\tmv.visitEnd();\n\t}\n\n\tprivate static void buildArgArray(String fullName, MethodVisitor mv, Arg[] args, Class<?>[] params) {\n\t\tint frameIndex = 1;\n\t\tfor (int i = 0; i < params.length; i++) {\n\t\t\tmv.visitVarInsn(ALOAD, 0);\n\t\t\tmv.visitFieldInsn(GETFIELD, fullName, args[i].name, args[i].desc);\n\t\t\tframeIndex += WriterHelper.storeToLocalVariable(mv, params[i], frameIndex);\n\t\t}\n\t\tWriterHelper.buildParamArray(mv, params);\n\t}\n\n\tprivate void buildCreateEmpty() {\n\t\tMethodVisitor mv = cw.visitMethod(ACC_PUBLIC, \"createEmpty\", \"()Lorg/yx/bean/aop/asm/ParamPojo;\", null, null);\n\t\tmv.visitCode();\n\t\tif (this.p.getArgNames().length == 0) {\n\t\t\tmv.visitVarInsn(ALOAD, 0);\n\t\t} else {\n\t\t\tmv.visitTypeInsn(NEW, this.fullName);\n\t\t\tmv.visitInsn(DUP);\n\t\t\tmv.visitMethodInsn(INVOKESPECIAL, this.fullName, \"<init>\", \"()V\", false);\n\t\t}\n\t\tmv.visitInsn(ARETURN);\n\t\tmv.visitMaxs(1, 1);\n\t\tmv.visitEnd();\n\t}\n\n\tprivate void buildInvoke() {\n\t\tMethodVisitor mv = cw.visitMethod(ACC_PUBLIC, \"invoke\", \"(Ljava/lang/Object;)Ljava/lang/Object;\", null,\n\t\t\t\tnew String[] { \"java/lang/Throwable\" });\n\t\tmv.visitCode();\n\t\tClass<?> ownerClz = method.getDeclaringClass();\n\t\tfinal String ownerName = ownerClz.getName().replace('.', '/');\n\n\t\tmv.visitLdcInsn(org.objectweb.asm.Type.getType(ownerClz));\n\t\tmv.visitVarInsn(ALOAD, 1);\n\t\tmv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, \"java/lang/Class\", \"cast\", \"(Ljava/lang/Object;)Ljava/lang/Object;\",\n\t\t\t\tfalse);\n\t\tmv.visitTypeInsn(Opcodes.CHECKCAST, ownerName);\n\t\tloadObjectFields(fullName, mv, args, method.getParameterTypes());\n\t\tmv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, ownerName, method.getName(), p.getMethodDesc(), false);\n\t\tClass<?> returnType = method.getReturnType();\n\t\tif (void.class == method.getReturnType()) {\n\t\t\tmv.visitInsn(Opcodes.ACONST_NULL);\n\t\t\tLogs.asm().debug(\"{} has no return\", fullName);\n\t\t} else if (returnType.isPrimitive()) {\n\t\t\tWriterHelper.boxPrimitive(mv, returnType);\n\t\t}\n\t\tmv.visitInsn(ARETURN);\n\t\tmv.visitMaxs(1, 0);\n\t\tmv.visitEnd();\n\t}\n\n\tprivate static void loadObjectFields(String fullName, MethodVisitor mv, Arg[] args, Class<?>[] params) {\n\t\tfor (int i = 0; i < params.length; i++) {\n\t\t\tmv.visitVarInsn(ALOAD, 0);\n\t\t\tmv.visitFieldInsn(GETFIELD, fullName, args[i].name, args[i].desc);\n\t\t}\n\t}\n\n\tprivate void buildSetParams() {\n\t\tMethodVisitor mv = cw.visitMethod(ACC_PUBLIC, \"setParams\", \"([Ljava/lang/Object;)V\", null, null);\n\t\tmv.visitCode();\n\n\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\tArg arg = args[i];\n\t\t\tClass<?> argType = method.getParameterTypes()[i];\n\t\t\tLabel label1 = new Label();\n\t\t\tif (argType.isPrimitive()) {\n\t\t\t\tmv.visitVarInsn(ALOAD, 1);\n\t\t\t\tWriterHelper.visitInt(mv, i);\n\t\t\t\tmv.visitInsn(AALOAD);\n\t\t\t\tmv.visitJumpInsn(IFNULL, label1);\n\t\t\t}\n\t\t\tmv.visitVarInsn(ALOAD, 0);\n\t\t\tmv.visitVarInsn(ALOAD, 1);\n\t\t\tWriterHelper.visitInt(mv, i);\n\t\t\tmv.visitInsn(AALOAD);\n\t\t\tcheckCast(mv, argType, arg.desc);\n\t\t\tmv.visitFieldInsn(PUTFIELD, this.fullName, arg.name, arg.desc);\n\t\t\tif (argType.isPrimitive()) {\n\t\t\t\tmv.visitLabel(label1);\n\t\t\t\tmv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);\n\t\t\t}\n\t\t}\n\n\t\tmv.visitInsn(RETURN);\n\t\tmv.visitMaxs(1, 2);\n\t\tmv.visitEnd();\n\t}\n\n\tprivate void checkCast(MethodVisitor mv, Class<?> argType, String desc) {\n\t\tif (!argType.isPrimitive()) {\n\t\t\tmv.visitTypeInsn(CHECKCAST, argType.getName().replace('.', '/'));\n\t\t\treturn;\n\t\t}\n\t\tif (argType == char.class) {\n\t\t\tmv.visitTypeInsn(CHECKCAST, \"java/lang/Character\");\n\t\t\tmv.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/Character\", \"charValue\", \"()C\", false);\n\t\t\treturn;\n\t\t}\n\t\tif (argType == boolean.class) {\n\t\t\tmv.visitTypeInsn(CHECKCAST, \"java/lang/Boolean\");\n\t\t\tmv.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/Boolean\", \"booleanValue\", \"()Z\", false);\n\t\t\treturn;\n\t\t}\n\t\tmv.visitTypeInsn(CHECKCAST, \"java/lang/Number\");\n\t\tmv.visitMethodInsn(INVOKEVIRTUAL, \"java/lang/Number\", argType.getName() + \"Value\", \"()\" + desc, false);\n\t}\n\n\tstatic class Arg {\n\t\tfinal String name;\n\t\tfinal String desc;\n\n\t\tprivate Arg(String name, String desc) {\n\t\t\tthis.name = name;\n\t\t\tthis.desc = desc;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/ParamPojos.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\nimport java.lang.reflect.Type;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class ParamPojos {\n\tprivate static Map<Class<? extends ParamPojo>, MethodPojo> map = new ConcurrentHashMap<>();\n\n\tpublic static MethodPojo get(Class<? extends ParamPojo> clz) {\n\t\treturn map.get(clz);\n\t}\n\n\tpublic synchronized static MethodPojo create(MethodParamInfo p) throws Exception {\n\t\tClass<? extends ParamPojo> clz = new ParamPojoClassFactory(p).create();\n\t\tMethodPojo info = map.get(clz);\n\t\tif (info != null) {\n\t\t\treturn info;\n\t\t}\n\t\tType[] fs = new Type[p.getArgNames().length];\n\t\tfor (int i = 0; i < fs.length; i++) {\n\t\t\tfs[i] = clz.getDeclaredField(p.getArgNames()[i]).getGenericType();\n\t\t}\n\t\tinfo = new MethodPojo(clz, p.getArgNames(), fs);\n\t\tmap.put(info.paramClz(), info);\n\t\treturn info;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/ParseParamsMethodVisitor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Type;\nimport org.slf4j.Logger;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\n\npublic class ParseParamsMethodVisitor extends MethodVisitor {\n\n\tprivate final MethodParamInfo info;\n\n\tprivate final List<LocalArg> argPojos;\n\tprivate final int maxIndex;\n\n\tpublic ParseParamsMethodVisitor(int api, MethodParamInfo info) {\n\t\tsuper(api);\n\t\tthis.info = info;\n\t\tthis.maxIndex = info.getArgNames().length * 2;\n\t\tthis.argPojos = new ArrayList<>(maxIndex);\n\t}\n\n\t@Override\n\tpublic void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {\n\n\t\tif (index == 0 && \"this\".equals(name)) {\n\t\t\treturn;\n\t\t}\n\t\tif (index >= maxIndex) {\n\t\t\treturn;\n\t\t}\n\t\targPojos.add(new LocalArg(name, desc, index, signature));\n\t}\n\n\t@Override\n\tpublic void visitEnd() {\n\t\tLogger log = Logs.asm();\n\t\tString methodName = info.getMethod().getName();\n\t\tType[] args = info.getArgumentTypes();\n\t\tCollections.sort(argPojos);\n\t\tif (log.isTraceEnabled()) {\n\t\t\tlog.trace(\"{}.{}():{}\", info.getMethod().getDeclaringClass().getName(), methodName, argPojos);\n\t\t}\n\t\tString[] argNames = info.getArgNames();\n\t\tString[] signatures = info.getSignatures();\n\t\tint size = argNames.length;\n\t\tif (argPojos.size() < size) {\n\t\t\tlog.error(\"{}.{},real param size:{},but is {}\", info.getMethod().getDeclaringClass().getName(), methodName,\n\t\t\t\t\tsize, argPojos.size());\n\t\t\tthrow new SumkException(9123253, \"failed to parse parameter because parameter size not satisfied\");\n\t\t}\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tLocalArg pojo = argPojos.get(i);\n\t\t\tif (!args[i].getDescriptor().equals(pojo.desc)) {\n\t\t\t\tlog.error(\"{}.{},i:{},index:{},except:{},indeed:{}\", info.getMethod().getDeclaringClass().getName(),\n\t\t\t\t\t\tmethodName, i, pojo.index, args[i].getDescriptor(), pojo.desc);\n\t\t\t\tthrow new SumkException(9123253, \"failed to parse parameter\");\n\t\t\t}\n\t\t\targNames[i] = pojo.name;\n\t\t\tsignatures[i] = pojo.signature;\n\t\t}\n\t}\n\n\tprivate static class LocalArg implements Comparable<LocalArg> {\n\t\tfinal String name;\n\t\tfinal String desc;\n\t\tfinal int index;\n\t\tfinal String signature;\n\n\t\tpublic LocalArg(String name, String desc, int index, String signature) {\n\t\t\tthis.name = name;\n\t\t\tthis.desc = desc;\n\t\t\tthis.index = index;\n\t\t\tthis.signature = signature;\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(LocalArg o) {\n\t\t\treturn index - o.index;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"{name=\" + name + \", desc=\" + desc + \", index=\" + index + \", signature=\" + signature + \"}\";\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/ProxyClassLoader.java",
    "content": "package org.yx.bean.aop.asm;\n\npublic class ProxyClassLoader extends ClassLoader {\n\n\tpublic ProxyClassLoader(ClassLoader parent) {\n\t\tsuper(parent);\n\t}\n\n\tpublic Class<?> defineClass(String name, byte[] clzByts) {\n\t\treturn super.defineClass(name, clzByts, 0, clzByts.length);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ProxyClassLoader [parent :\" + getParent() + \"]@\" + Integer.toHexString(hashCode());\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/ProxyClassVistor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\nimport static org.objectweb.asm.Opcodes.ACC_ABSTRACT;\nimport static org.objectweb.asm.Opcodes.ACC_FINAL;\nimport static org.objectweb.asm.Opcodes.ACC_PRIVATE;\nimport static org.objectweb.asm.Opcodes.ACC_PUBLIC;\nimport static org.objectweb.asm.Opcodes.ACC_STATIC;\nimport static org.objectweb.asm.Opcodes.ALOAD;\nimport static org.objectweb.asm.Opcodes.ARETURN;\nimport static org.objectweb.asm.Opcodes.INVOKESPECIAL;\nimport static org.objectweb.asm.Opcodes.RETURN;\n\nimport java.lang.reflect.Method;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.objectweb.asm.AnnotationVisitor;\nimport org.objectweb.asm.Attribute;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.FieldVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.TypePath;\nimport org.yx.bean.aop.asm.ProxyMethodWritor.AsmMethod;\n\npublic class ProxyClassVistor extends ClassVisitor {\n\n\tprivate final Class<?> orginClz;\n\tprivate final Map<Method, Integer> aopMethods;\n\tprivate final String clzName;\n\n\tpublic ProxyClassVistor(final ClassVisitor cv, String newClzName, Class<?> clz, Map<Method, Integer> aopMethods) {\n\t\tsuper(AsmUtils.asmVersion(), cv);\n\t\tthis.orginClz = clz;\n\t\tthis.aopMethods = aopMethods;\n\t\tthis.clzName = newClzName;\n\t}\n\n\t@Override\n\tpublic void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n\n\t\tsuper.visit(version, access, clzName.replace('.', '/'), signature, name, new String[] { \"org/yx/bean/Boxed\" });\n\n\t\tMethodVisitor mv = super.visitMethod(ACC_PUBLIC, \"<init>\", \"()V\", null, null);\n\t\tmv.visitCode();\n\t\tmv.visitVarInsn(ALOAD, 0);\n\t\tmv.visitMethodInsn(INVOKESPECIAL, name, \"<init>\", \"()V\", false);\n\t\tmv.visitInsn(RETURN);\n\t\tmv.visitMaxs(1, 1);\n\t\tmv.visitEnd();\n\t}\n\n\tprivate Entry<Method, Integer> queryMethod(String name, String desc) {\n\t\tfor (Entry<Method, Integer> entry : aopMethods.entrySet()) {\n\t\t\tMethod m = entry.getKey();\n\n\t\t\tif (m.getName().equals(name) && desc.equals(Type.getMethodDescriptor(m))) {\n\t\t\t\treturn entry;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {\n\n\t\tif (\"<init>\".equals(name)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tint badModifiers = ACC_STATIC | ACC_FINAL | ACC_ABSTRACT | ACC_PRIVATE;\n\t\tif ((access & badModifiers) != 0) {\n\t\t\treturn null;\n\t\t}\n\t\tEntry<Method, Integer> methodEntry = queryMethod(name, desc);\n\t\tif (methodEntry != null) {\n\n\t\t\tMethodVisitor mv = cv.visitMethod(access, name, desc, signature, null);\n\t\t\tAsmMethod asmMethod = new AsmMethod(access, name, desc, signature, exceptions, methodEntry.getKey(),\n\t\t\t\t\tmethodEntry.getValue().intValue(), clzName, this.orginClz);\n\t\t\tProxyMethodWritor.write(mv, asmMethod);\n\n\t\t\treturn null;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void visitOuterClass(String owner, String name, String desc) {\n\t}\n\n\t@Override\n\tpublic AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void visitAttribute(Attribute attr) {\n\t}\n\n\t@Override\n\tpublic void visitInnerClass(String name, String outerName, String innerName, int access) {\n\t}\n\n\t@Override\n\tpublic FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void visitEnd() {\n\t\tMethodVisitor mv = super.visitMethod(ACC_PUBLIC, \"targetRawClass\", \"()Ljava/lang/Class;\",\n\t\t\t\t\"()Ljava/lang/Class<*>;\", null);\n\t\tmv.visitCode();\n\t\tmv.visitLdcInsn(Type.getType(this.orginClz));\n\t\tmv.visitInsn(ARETURN);\n\t\tmv.visitMaxs(1, 1);\n\t\tmv.visitEnd();\n\t\tsuper.visitEnd();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/ProxyMethodWritor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\nimport static org.objectweb.asm.Opcodes.ACONST_NULL;\nimport static org.objectweb.asm.Opcodes.ALOAD;\nimport static org.objectweb.asm.Opcodes.ARETURN;\nimport static org.objectweb.asm.Opcodes.ASTORE;\nimport static org.objectweb.asm.Opcodes.ATHROW;\nimport static org.objectweb.asm.Opcodes.DRETURN;\nimport static org.objectweb.asm.Opcodes.DUP;\nimport static org.objectweb.asm.Opcodes.FRETURN;\nimport static org.objectweb.asm.Opcodes.ICONST_0;\nimport static org.objectweb.asm.Opcodes.ICONST_1;\nimport static org.objectweb.asm.Opcodes.INVOKESPECIAL;\nimport static org.objectweb.asm.Opcodes.INVOKESTATIC;\nimport static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;\nimport static org.objectweb.asm.Opcodes.IRETURN;\nimport static org.objectweb.asm.Opcodes.LRETURN;\nimport static org.objectweb.asm.Opcodes.NEW;\nimport static org.objectweb.asm.Opcodes.RETURN;\nimport static org.yx.bean.aop.asm.WriterHelper.SINGLE;\nimport static org.yx.bean.aop.asm.WriterHelper.WIDTH;\nimport static org.yx.bean.aop.asm.WriterHelper.buildParamArray;\nimport static org.yx.bean.aop.asm.WriterHelper.loadFromLocalVariable;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\n\npublic final class ProxyMethodWritor {\n\n\tprivate static void jReturn(MethodVisitor mv, Class<?> c) {\n\t\tif (c == Integer.TYPE || c == Boolean.TYPE || c == Byte.TYPE || c == Character.TYPE || c == Short.TYPE) {\n\t\t\tmv.visitInsn(IRETURN);\n\t\t} else if (c == Float.TYPE) {\n\t\t\tmv.visitInsn(FRETURN);\n\t\t} else if (c == Double.TYPE) {\n\t\t\tmv.visitInsn(DRETURN);\n\t\t} else if (c == Long.TYPE) {\n\t\t\tmv.visitInsn(LRETURN);\n\t\t} else {\n\t\t\tmv.visitInsn(ARETURN);\n\t\t}\n\t}\n\n\tprivate static int argLength(Class<?>[] params) {\n\t\tint size = 0;\n\t\tfor (Class<?> param : params) {\n\t\t\tsize += (param == Double.TYPE || param == Long.TYPE) ? WIDTH : SINGLE;\n\t\t}\n\t\treturn size;\n\t}\n\n\tprivate static class ProxyBuilder {\n\t\tprivate static final String AOP_EXCUTOR_CHAIN = \"org/yx/bean/aop/AopExecutorChain\";\n\n\t\tprivate final MethodVisitor mv;\n\t\tprivate final AsmMethod asmMethod;\n\t\tprivate final Class<?>[] params;\n\t\tprivate final Class<?> returnType;\n\t\tprivate final String superowener;\n\n\t\tprivate final int aopExcutorChainIndex;\n\n\t\tprivate void jReturn() {\n\t\t\tProxyMethodWritor.jReturn(mv, returnType);\n\t\t}\n\n\t\tprivate void jReturnNULLOrException() {\n\t\t\tif (!returnType.isPrimitive()) {\n\t\t\t\tmv.visitInsn(ACONST_NULL);\n\t\t\t\tmv.visitInsn(ARETURN);\n\t\t\t} else {\n\t\t\t\tmv.visitTypeInsn(NEW, \"org/yx/exception/SumkException\");\n\t\t\t\tmv.visitInsn(DUP);\n\t\t\t\tmv.visitLdcInsn(new Integer(-912753901));\n\t\t\t\tmv.visitLdcInsn(\"返回值为原始类型的方法，不支持aop屏蔽异常\");\n\t\t\t\tmv.visitMethodInsn(INVOKESPECIAL, \"org/yx/exception/SumkException\", \"<init>\", \"(ILjava/lang/String;)V\",\n\t\t\t\t\t\tfalse);\n\t\t\t\tmv.visitInsn(ATHROW);\n\t\t\t}\n\t\t}\n\n\t\tprivate int storeReuturnToLocalVariable(int frameIndex) {\n\t\t\treturn WriterHelper.storeToLocalVariable(mv, returnType, frameIndex);\n\t\t}\n\n\t\tprivate void loadArgs() {\n\t\t\tint frameIndex = 1;\n\t\t\tfor (Class<?> argType : params) {\n\t\t\t\tframeIndex += loadFromLocalVariable(mv, argType, frameIndex, false);\n\t\t\t}\n\t\t}\n\n\t\tpublic ProxyBuilder(MethodVisitor mv, AsmMethod asmMethod) {\n\t\t\tthis.asmMethod = asmMethod;\n\t\t\tthis.mv = mv;\n\t\t\tparams = asmMethod.method.getParameterTypes();\n\t\t\treturnType = asmMethod.method.getReturnType();\n\t\t\tsuperowener = Type.getInternalName(asmMethod.superClz);\n\t\t\tint paramsVariableIndex = argLength(params);\n\t\t\taopExcutorChainIndex = paramsVariableIndex + 1;\n\t\t}\n\n\t\tprivate void callSuperMethod() {\n\t\t\tmv.visitVarInsn(ALOAD, 0);\n\t\t\tthis.loadArgs();\n\t\t\tmv.visitMethodInsn(INVOKESPECIAL, superowener, asmMethod.name, asmMethod.desc, false);\n\t\t}\n\n\t\tprivate void writeVoidMethod(int key) {\n\t\t\tLabel l3 = new Label();\n\t\t\tLabel label_before_1 = new Label();\n\t\t\tLabel label_biz_executed = new Label();\n\t\t\tmv.visitTryCatchBlock(label_before_1, label_biz_executed, l3, \"java/lang/Throwable\");\n\n\t\t\tWriterHelper.visitInt(mv, key);\n\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"org/yx/bean/InnerIOC\", \"getAopExecutorChain\",\n\t\t\t\t\t\"(I)Lorg/yx/bean/aop/AopExecutorChain;\", false);\n\t\t\tmv.visitVarInsn(ASTORE, this.aopExcutorChainIndex);\n\t\t\tmv.visitLabel(label_before_1);\n\t\t\tmv.visitVarInsn(ALOAD, this.aopExcutorChainIndex);\n\t\t\tbuildParamArray(mv, params);\n\t\t\tthis.visitBefore();\n\n\t\t\tthis.callSuperMethod();\n\t\t\tmv.visitLabel(label_biz_executed);\n\t\t\tmv.visitVarInsn(ALOAD, this.aopExcutorChainIndex);\n\t\t\tmv.visitInsn(ACONST_NULL);\n\t\t\tmv.visitInsn(ACONST_NULL);\n\t\t\tmv.visitInsn(ICONST_1);\n\t\t\tthis.visitAfter();\n\t\t\tmv.visitInsn(RETURN);\n\n\t\t\tmv.visitLabel(l3);\n\t\t\tthis.visitFullFrame();\n\t\t\tint exceptionIndex = this.aopExcutorChainIndex + 1;\n\t\t\tmv.visitVarInsn(ASTORE, exceptionIndex);\n\t\t\tmv.visitVarInsn(ALOAD, this.aopExcutorChainIndex);\n\t\t\tmv.visitInsn(ACONST_NULL);\n\t\t\tmv.visitVarInsn(ALOAD, exceptionIndex);\n\t\t\tmv.visitInsn(ICONST_0);\n\t\t\tvisitAfter();\n\t\t\tmv.visitInsn(RETURN);\n\n\t\t}\n\n\t\tpublic void write() {\n\t\t\tint index = this.asmMethod.excutorSupplierIndex;\n\t\t\tmv.visitCode();\n\n\t\t\tboolean hasReturn = returnType != null && returnType != Void.TYPE;\n\t\t\tif (hasReturn) {\n\t\t\t\tthis.writeWithReturn(index);\n\t\t\t} else {\n\t\t\t\tthis.writeVoidMethod(index);\n\t\t\t}\n\n\t\t\tmv.visitMaxs(1, 1);\n\t\t\tmv.visitEnd();\n\t\t}\n\n\t\tprivate void visitAfter() {\n\t\t\tmv.visitMethodInsn(INVOKEVIRTUAL, AOP_EXCUTOR_CHAIN, \"after\", \"(Ljava/lang/Object;Ljava/lang/Throwable;Z)V\",\n\t\t\t\t\tfalse);\n\t\t}\n\n\t\tprivate void visitBefore() {\n\t\t\tmv.visitMethodInsn(INVOKEVIRTUAL, AOP_EXCUTOR_CHAIN, \"before\", \"([Ljava/lang/Object;)V\", false);\n\t\t}\n\n\t\tprivate void writeWithReturn(int key) {\n\t\t\tLabel l3 = new Label();\n\t\t\tLabel label_before_1 = new Label();\n\t\t\tLabel label_biz_executed = new Label();\n\t\t\tmv.visitTryCatchBlock(label_before_1, label_biz_executed, l3, \"java/lang/Throwable\");\n\t\t\tWriterHelper.visitInt(mv, key);\n\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"org/yx/bean/InnerIOC\", \"getAopExecutorChain\",\n\t\t\t\t\t\"(I)Lorg/yx/bean/aop/AopExecutorChain;\", false);\n\t\t\tmv.visitVarInsn(ASTORE, this.aopExcutorChainIndex);\n\t\t\tmv.visitLabel(label_before_1);\n\t\t\tmv.visitVarInsn(ALOAD, this.aopExcutorChainIndex);\n\t\t\tbuildParamArray(mv, params);\n\t\t\tthis.visitBefore();\n\n\t\t\tthis.callSuperMethod();\n\t\t\tmv.visitLabel(label_biz_executed);\n\t\t\tint returnValueIndex = this.aopExcutorChainIndex + 1;\n\t\t\tstoreReuturnToLocalVariable(returnValueIndex);\n\n\t\t\tmv.visitVarInsn(ALOAD, this.aopExcutorChainIndex);\n\t\t\tloadFromLocalVariable(mv, this.returnType, returnValueIndex, true);\n\t\t\tmv.visitInsn(ACONST_NULL);\n\t\t\tmv.visitInsn(ICONST_1);\n\t\t\tvisitAfter();\n\t\t\tloadFromLocalVariable(mv, this.returnType, this.aopExcutorChainIndex + 1, false);\n\t\t\tthis.jReturn();\n\n\t\t\tint exceptionIndex = this.aopExcutorChainIndex + 1;\n\t\t\tmv.visitLabel(l3);\n\t\t\tthis.visitFullFrame();\n\t\t\tmv.visitVarInsn(ASTORE, exceptionIndex);\n\t\t\tmv.visitVarInsn(ALOAD, this.aopExcutorChainIndex);\n\t\t\tmv.visitInsn(ACONST_NULL);\n\t\t\tmv.visitVarInsn(ALOAD, exceptionIndex);\n\t\t\tmv.visitInsn(ICONST_0);\n\t\t\tvisitAfter();\n\n\t\t\tthis.jReturnNULLOrException();\n\n\t\t}\n\n\t\tprivate void visitFullFrame() {\n\t\t\tString currentClz = asmMethod.currentClz.replace('.', '/');\n\t\t\tList<Object> argTypes = AsmUtils.getImplicitFrame(\n\t\t\t\t\tasmMethod.desc.substring(asmMethod.desc.indexOf(\"(\") + 1, asmMethod.desc.indexOf(\")\")));\n\t\t\tList<Object> list = new ArrayList<>();\n\t\t\tlist.add(currentClz);\n\t\t\tlist.addAll(argTypes);\n\t\t\tlist.add(AOP_EXCUTOR_CHAIN);\n\t\t\tObject[] frames = list.toArray(new Object[list.size()]);\n\t\t\tmv.visitFrame(Opcodes.F_FULL, frames.length, frames, 1, new Object[] { \"java/lang/Throwable\" });\n\t\t}\n\t}\n\n\tpublic static void write(MethodVisitor mv, AsmMethod asmMethod) {\n\t\tnew ProxyBuilder(mv, asmMethod).write();\n\t}\n\n\tstatic class AsmMethod {\n\t\tfinal int access;\n\t\tfinal String name;\n\t\tfinal String desc;\n\t\tfinal String signature;\n\t\tfinal String[] exceptions;\n\t\tfinal Method method;\n\t\tfinal String currentClz;\n\t\tfinal Class<?> superClz;\n\t\tfinal int excutorSupplierIndex;\n\n\t\tpublic AsmMethod(int access, String name, String desc, String signature, String[] exceptions, Method method,\n\t\t\t\tint advisors, String currentClz, Class<?> supperClz) {\n\t\t\tthis.access = access;\n\t\t\tthis.name = name;\n\t\t\tthis.desc = desc;\n\t\t\tthis.signature = signature;\n\t\t\tthis.exceptions = exceptions;\n\t\t\tthis.method = method;\n\t\t\tthis.currentClz = currentClz;\n\t\t\tthis.superClz = supperClz;\n\t\t\tthis.excutorSupplierIndex = advisors;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/asm/WriterHelper.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.asm;\n\nimport static org.objectweb.asm.Opcodes.AASTORE;\nimport static org.objectweb.asm.Opcodes.ALOAD;\nimport static org.objectweb.asm.Opcodes.ANEWARRAY;\nimport static org.objectweb.asm.Opcodes.ASTORE;\nimport static org.objectweb.asm.Opcodes.BIPUSH;\nimport static org.objectweb.asm.Opcodes.DLOAD;\nimport static org.objectweb.asm.Opcodes.DSTORE;\nimport static org.objectweb.asm.Opcodes.DUP;\nimport static org.objectweb.asm.Opcodes.FLOAD;\nimport static org.objectweb.asm.Opcodes.FSTORE;\nimport static org.objectweb.asm.Opcodes.ICONST_0;\nimport static org.objectweb.asm.Opcodes.ILOAD;\nimport static org.objectweb.asm.Opcodes.INVOKESTATIC;\nimport static org.objectweb.asm.Opcodes.ISTORE;\nimport static org.objectweb.asm.Opcodes.LLOAD;\nimport static org.objectweb.asm.Opcodes.LSTORE;\nimport static org.objectweb.asm.Opcodes.SIPUSH;\n\nimport org.objectweb.asm.MethodVisitor;\nimport org.yx.conf.AppInfo;\n\npublic final class WriterHelper {\n\tpublic static final int SINGLE = AppInfo.getInt(\"sumk.asm.single\", 1);\n\tpublic static final int WIDTH = AppInfo.getInt(\"sumk.asm.width\", 2);\n\n\tpublic static void visitInt(MethodVisitor mv, int num) {\n\t\tif (num >= -1 && num <= 5) {\n\t\t\tmv.visitInsn(ICONST_0 + num);\n\t\t\treturn;\n\t\t}\n\t\tif (num <= Byte.MAX_VALUE && num >= Byte.MIN_VALUE) {\n\t\t\tmv.visitIntInsn(BIPUSH, num);\n\t\t\treturn;\n\t\t}\n\t\tif (num <= Short.MAX_VALUE && num >= Short.MIN_VALUE) {\n\t\t\tmv.visitIntInsn(SIPUSH, num);\n\t\t\treturn;\n\t\t}\n\t\tmv.visitLdcInsn(num);\n\t}\n\n\tpublic static void buildParamArray(MethodVisitor mv, Class<?>[] params) {\n\t\tfinal int len = params == null ? 0 : params.length;\n\t\tvisitInt(mv, len);\n\n\t\tmv.visitTypeInsn(ANEWARRAY, \"java/lang/Object\");\n\t\tif (len == 0) {\n\t\t\treturn;\n\t\t}\n\t\tint frameIndex = 1;\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tmv.visitInsn(DUP);\n\t\t\tvisitInt(mv, i);\n\t\t\tClass<?> argType = params[i];\n\t\t\tframeIndex += loadFromLocalVariable(mv, argType, frameIndex, true);\n\t\t\tmv.visitInsn(AASTORE);\n\t\t}\n\t}\n\n\tpublic static int loadFromLocalVariable(MethodVisitor mv, Class<?> c, int frameIndex, boolean autoBox) {\n\t\tif (c == Integer.TYPE) {\n\t\t\tmv.visitVarInsn(ILOAD, frameIndex);\n\t\t\tif (autoBox) {\n\t\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Integer\", \"valueOf\", \"(I)Ljava/lang/Integer;\", false);\n\t\t\t}\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Boolean.TYPE) {\n\t\t\tmv.visitVarInsn(ILOAD, frameIndex);\n\t\t\tif (autoBox) {\n\t\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Boolean\", \"valueOf\", \"(Z)Ljava/lang/Boolean;\", false);\n\t\t\t}\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Byte.TYPE) {\n\t\t\tmv.visitVarInsn(ILOAD, frameIndex);\n\t\t\tif (autoBox) {\n\t\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Byte\", \"valueOf\", \"(B)Ljava/lang/Byte;\", false);\n\t\t\t}\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Character.TYPE) {\n\t\t\tmv.visitVarInsn(ILOAD, frameIndex);\n\t\t\tif (autoBox) {\n\t\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Character\", \"valueOf\", \"(C)Ljava/lang/Character;\", false);\n\t\t\t}\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Short.TYPE) {\n\t\t\tmv.visitVarInsn(ILOAD, frameIndex);\n\t\t\tif (autoBox) {\n\t\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Short\", \"valueOf\", \"(S)Ljava/lang/Short;\", false);\n\t\t\t}\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Float.TYPE) {\n\t\t\tmv.visitVarInsn(FLOAD, frameIndex);\n\t\t\tif (autoBox) {\n\t\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Float\", \"valueOf\", \"(F)Ljava/lang/Float;\", false);\n\t\t\t}\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Double.TYPE) {\n\t\t\tmv.visitVarInsn(DLOAD, frameIndex);\n\t\t\tif (autoBox) {\n\t\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Double\", \"valueOf\", \"(D)Ljava/lang/Double;\", false);\n\t\t\t}\n\t\t\treturn WIDTH;\n\t\t}\n\t\tif (c == Long.TYPE) {\n\t\t\tmv.visitVarInsn(LLOAD, frameIndex);\n\t\t\tif (autoBox) {\n\t\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Long\", \"valueOf\", \"(J)Ljava/lang/Long;\", false);\n\t\t\t}\n\t\t\treturn WIDTH;\n\t\t}\n\n\t\tmv.visitVarInsn(ALOAD, frameIndex);\n\t\treturn SINGLE;\n\n\t}\n\n\tpublic static int boxPrimitive(MethodVisitor mv, Class<?> c) {\n\t\tif (c == Integer.TYPE) {\n\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Integer\", \"valueOf\", \"(I)Ljava/lang/Integer;\", false);\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Boolean.TYPE) {\n\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Boolean\", \"valueOf\", \"(Z)Ljava/lang/Boolean;\", false);\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Byte.TYPE) {\n\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Byte\", \"valueOf\", \"(B)Ljava/lang/Byte;\", false);\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Character.TYPE) {\n\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Character\", \"valueOf\", \"(C)Ljava/lang/Character;\", false);\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Short.TYPE) {\n\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Short\", \"valueOf\", \"(S)Ljava/lang/Short;\", false);\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Float.TYPE) {\n\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Float\", \"valueOf\", \"(F)Ljava/lang/Float;\", false);\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Double.TYPE) {\n\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Double\", \"valueOf\", \"(D)Ljava/lang/Double;\", false);\n\t\t\treturn WIDTH;\n\t\t}\n\t\tif (c == Long.TYPE) {\n\t\t\tmv.visitMethodInsn(INVOKESTATIC, \"java/lang/Long\", \"valueOf\", \"(J)Ljava/lang/Long;\", false);\n\t\t\treturn WIDTH;\n\t\t}\n\t\treturn 0;\n\n\t}\n\n\tpublic static int storeToLocalVariable(MethodVisitor mv, Class<?> c, int frameIndex) {\n\t\tif (c == Integer.TYPE || c == Boolean.TYPE || c == Byte.TYPE || c == Character.TYPE || c == Short.TYPE) {\n\t\t\tmv.visitVarInsn(ISTORE, frameIndex);\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Float.TYPE) {\n\t\t\tmv.visitVarInsn(FSTORE, frameIndex);\n\t\t\treturn SINGLE;\n\t\t}\n\t\tif (c == Double.TYPE) {\n\t\t\tmv.visitVarInsn(DSTORE, frameIndex);\n\t\t\treturn WIDTH;\n\t\t}\n\t\tif (c == Long.TYPE) {\n\t\t\tmv.visitVarInsn(LSTORE, frameIndex);\n\t\t\treturn WIDTH;\n\t\t}\n\n\t\tmv.visitVarInsn(ASTORE, frameIndex);\n\t\treturn SINGLE;\n\n\t}\n\n\tpublic static String boxDesc(String desc) {\n\t\tswitch (desc.charAt(0)) {\n\t\tcase 'Z':\n\t\t\treturn \"Ljava/lang/Boolean;\";\n\t\tcase 'C':\n\t\t\treturn \"Ljava/lang/Character;\";\n\t\tcase 'B':\n\t\t\treturn \"Ljava/lang/Byte;\";\n\t\tcase 'S':\n\t\t\treturn \"Ljava/lang/Short;\";\n\t\tcase 'I':\n\t\t\treturn \"Ljava/lang/Integer;\";\n\t\tcase 'F':\n\t\t\treturn \"Ljava/lang/Float;\";\n\t\tcase 'J':\n\t\t\treturn \"Ljava/lang/Long;\";\n\t\tcase 'D':\n\t\t\treturn \"Ljava/lang/Double;\";\n\t\tdefault:\n\t\t\treturn desc;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/context/CalleeNode.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.context;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedType;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.yx.annotation.doc.Comment;\nimport org.yx.annotation.spec.ParamSpec;\nimport org.yx.annotation.spec.Specs;\nimport org.yx.bean.aop.asm.ParamPojo;\nimport org.yx.common.validate.FieldParameterHolder;\nimport org.yx.common.validate.ParamInfo;\nimport org.yx.common.validate.Validators;\nimport org.yx.bean.aop.asm.MethodPojo;\nimport org.yx.log.Log;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.SumkThreadPool;\n\npublic abstract class CalleeNode {\n\n\tprotected final ParamInfo[] paramInfos;\n\n\tprotected final Object owner;\n\n\tprotected final MethodPojo params;\n\n\tprivate final int toplimit;\n\n\tprotected final Method method;\n\n\tpublic CalleeNode(Object owner, Method method, MethodPojo params, int toplimit) {\n\t\tthis.owner = Objects.requireNonNull(owner);\n\t\tthis.params = Objects.requireNonNull(params);\n\t\tthis.method = Objects.requireNonNull(method);\n\t\tParamSpec[] paramSpecs = Specs.extractParamParamter(method);\n\t\tthis.paramInfos = paramSpecs == null || paramSpecs.length == 0 ? null : new ParamInfo[paramSpecs.length];\n\t\tthis.toplimit = toplimit;\n\t\tif (this.paramInfos != null) {\n\t\t\tClass<?>[] argTypes = this.getParameterTypes();\n\t\t\tfor (int i = 0; i < this.paramInfos.length; i++) {\n\t\t\t\tParamSpec p = paramSpecs[i];\n\t\t\t\tif (p == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tparamInfos[i] = new ParamInfo(p, params.getParamName(i), argTypes[i]);\n\t\t\t}\n\t\t}\n\t\tregisterFieldInfos();\n\t}\n\n\tprotected void registerFieldInfos() {\n\t\tif (this.paramInfos == null) {\n\t\t\treturn;\n\t\t}\n\t\tfor (ParamInfo pf : this.paramInfos) {\n\t\t\tif (pf == null || !pf.isComplex()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tFieldParameterHolder.registerFieldInfo(pf.getParamType());\n\t\t}\n\t}\n\n\tpublic boolean overflowThreshold() {\n\t\tif (this.toplimit < SumkThreadPool.executor().threshold()) {\n\t\t\tif (Log.get(\"sumk.thread\").isDebugEnabled()) {\n\t\t\t\tString msg = new StringBuilder().append(\"[\")\n\t\t\t\t\t\t.append(this.getClass().getSimpleName().replace(\"ActionNode\", \"\")).append(\"] \")\n\t\t\t\t\t\t.append(this.method.getDeclaringClass().getSimpleName()).append(\".\")\n\t\t\t\t\t\t.append(this.method.getName()).append(\"() - priority=\").append(toplimit)\n\t\t\t\t\t\t.append(\" ,  threshold=\").append(SumkThreadPool.executor().threshold()).toString();\n\t\t\t\tLog.get(\"sumk.thread\").debug(msg);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic ParamPojo createEmptyParamObj() {\n\t\treturn this.params.createEmptyParamObj();\n\t}\n\n\tpublic Class<?> getDeclaringClass() {\n\t\treturn this.method.getDeclaringClass();\n\t}\n\n\tpublic Class<?> getReturnType() {\n\t\treturn this.method.getReturnType();\n\t}\n\n\tpublic String getMethodName() {\n\t\treturn this.method.getName();\n\t}\n\n\tpublic Class<?>[] getParameterTypes() {\n\t\treturn method.getParameterTypes();\n\t}\n\n\tpublic Method rawMethod() {\n\t\treturn this.method;\n\t}\n\n\tpublic <T extends Annotation> T getAnnotation(Class<T> annotationClass) {\n\t\treturn method.getAnnotation(annotationClass);\n\t}\n\n\tpublic Annotation[] getDeclaredAnnotations() {\n\t\treturn method.getDeclaredAnnotations();\n\t}\n\n\tpublic Annotation[][] getParameterAnnotations() {\n\t\treturn method.getParameterAnnotations();\n\t}\n\n\tpublic AnnotatedType getAnnotatedReturnType() {\n\t\treturn method.getAnnotatedReturnType();\n\t}\n\n\tpublic int toplimit() {\n\t\treturn this.toplimit;\n\t}\n\n\tpublic int paramLength() {\n\t\treturn this.params.paramLength();\n\t}\n\n\tpublic Object owner() {\n\t\treturn this.owner;\n\t}\n\n\tpublic Object execute(ParamPojo argObj) throws Throwable {\n\t\tif (this.paramInfos != null) {\n\t\t\tObject[] params = argObj.params();\n\t\t\tfor (int i = 0; i < paramInfos.length; i++) {\n\t\t\t\tParamInfo info = paramInfos[i];\n\t\t\t\tif (info != null) {\n\t\t\t\t\tValidators.check(info, params[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\treturn argObj.invoke(owner);\n\t\t} catch (Throwable e) {\n\n\t\t\tif (e instanceof InvocationTargetException) {\n\t\t\t\tInvocationTargetException te = (InvocationTargetException) e;\n\t\t\t\tif (te.getTargetException() != null) {\n\t\t\t\t\tthrow te.getTargetException();\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tpublic List<String> paramNames() {\n\t\treturn this.params.paramNames();\n\t}\n\n\tpublic List<ParamInfo> paramInfos() {\n\t\treturn CollectionUtil.unmodifyList(paramInfos);\n\t}\n\n\tpublic MethodPojo params() {\n\t\treturn this.params;\n\t}\n\n\tpublic String comment() {\n\t\tComment c = getAnnotation(Comment.class);\n\t\treturn c == null ? \"\" : c.value();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/aop/context/NodeContext.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.aop.context;\n\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.bean.aop.asm.ParamPojo;\n\npublic abstract class NodeContext<T extends CalleeNode> {\n\tprivate ParamPojo argPojo;\n\n\t@NotNull\n\tpublic abstract T node();\n\n\tpublic ParamPojo getParamPojo() {\n\t\treturn argPojo;\n\t}\n\n\tpublic void setParamPojo(ParamPojo argPojo) {\n\t\tthis.argPojo = Objects.requireNonNull(argPojo);\n\t}\n\n\t/**\n\t * @return 参数组成的map对象\n\t */\n\tpublic Map<String, Object> getParams() {\n\t\tObject[] args = argPojo.params();\n\t\tint len = args.length;\n\t\tList<String> names = node().paramNames();\n\t\tMap<String, Object> map = new LinkedHashMap<>();\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tString name = names.get(i);\n\t\t\tmap.put(name, args[i]);\n\t\t}\n\t\treturn map;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/watcher/BeanCreateWatcher.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.watcher;\n\nimport java.util.List;\n\nimport org.yx.base.Ordered;\n\npublic interface BeanCreateWatcher extends Ordered {\n\n\tvoid afterCreate(List<Object> beans);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/watcher/BeanInjectWatcher.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.bean.watcher;\n\nimport java.util.List;\n\nimport org.yx.base.Ordered;\n\n/**\n * 只被调用一次，beans参数是额外参数，如果用不到可以忽略它。 beans里的对象不一定是原始对象，有可能是代理后的对象，\n * 可通过是否实现Boxed接口来判断。\n */\npublic interface BeanInjectWatcher extends Ordered {\n\n\tvoid afterInject(List<Object> beans);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/bean/watcher/BootWatcher.java",
    "content": "package org.yx.bean.watcher;\n\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.yx.base.Ordered;\n\n/**\n * 实现这个接口的类，必须要有一个无参的构造函数，这个接口与Bean无关，不需要@Bean注解。<BR>\n * ioc启动主要由本对象的publish方法完成，本接口的实例不属于IOC的bean。<BR>\n * 如果想要排除掉某个BootWatcher，可以通过sumk.ioc.booter.exclude配置来实现，\n * 也可以通过sumk.ioc.exclude.XX来配置\n */\npublic interface BootWatcher extends Ordered {\n\n\t/**\n\t * ioc启动工作，这个阶段还不能使用数据库、redis等功能\n\t * \n\t * @param sortedClasses 扫描出来的class列表,这个列表已经排好优先级顺序了。只读\n\t * @param optional      如果加载失败就忽略的类名的判定器\n\t * @return 修改后的class列表，或者null。一般返回null就行了\n\t * @throws Exception 如果抛出异常，就表示启动失败\n\t */\n\tList<Class<?>> publish(List<Class<?>> sortedClasses, Predicate<String> optional) throws Exception;\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/Host.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common;\n\nimport java.net.InetSocketAddress;\nimport java.util.Objects;\n\npublic final class Host implements Comparable<Host> {\n\tprivate final String ip;\n\tprivate final int port;\n\n\tprivate Host(String ip, int port) {\n\t\tthis.ip = Objects.requireNonNull(ip);\n\t\tthis.port = port;\n\t}\n\n\tpublic static Host create(String addr) {\n\t\tint index = addr.lastIndexOf(':');\n\t\tif (index < 1) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new Host(addr.substring(0, index), Integer.valueOf(addr.substring(index + 1)));\n\t}\n\n\tpublic static Host create(String ip, int port) {\n\t\treturn new Host(ip, port);\n\t}\n\n\tpublic String ip() {\n\t\treturn ip;\n\t}\n\n\tpublic int port() {\n\t\treturn port;\n\t}\n\n\tpublic InetSocketAddress toInetSocketAddress() {\n\t\treturn new InetSocketAddress(ip, port);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((ip == null) ? 0 : ip.hashCode());\n\t\tresult = prime * result + port;\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tHost other = (Host) obj;\n\t\tif (ip == null) {\n\t\t\tif (other.ip != null)\n\t\t\t\treturn false;\n\t\t} else if (!ip.equals(other.ip))\n\t\t\treturn false;\n\t\tif (port != other.port)\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n\tpublic String toAddressString() {\n\t\treturn new StringBuilder().append(ip).append(':').append(port).toString();\n\t}\n\n\t/**\n\t * 返回 ip + \":\" + port格式， 这个方法也比较重要，所以它的格式不会发生变更\n\t */\n\t@Override\n\tpublic String toString() {\n\t\treturn toAddressString();\n\t}\n\n\t@Override\n\tpublic int compareTo(Host h) {\n\t\tint t = ip.compareTo(h.ip);\n\t\tif (t != 0) {\n\t\t\treturn t;\n\t\t}\n\t\treturn port - h.port;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/Predicator.java",
    "content": "package org.yx.common;\n\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.yx.util.StringUtil;\n\npublic class Predicator {\n\n\tpublic static boolean test(String vs, Predicate<String> p) {\n\t\tif (vs.contains(\"||\")) {\n\t\t\treturn orTest(vs, p);\n\t\t}\n\t\treturn andTest(vs, p);\n\t}\n\n\tpublic static boolean andTest(String vs, Predicate<String> p) {\n\t\tList<String> list = StringUtil.splitAndTrim(vs, \",\", \"，\", \"&&\");\n\t\tfor (String s : list) {\n\t\t\tif (!p.test(s)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static boolean orTest(String vs, Predicate<String> p) {\n\t\tList<String> list = StringUtil.splitAndTrim(vs, \"\\\\|\\\\|\");\n\t\tfor (String s : list) {\n\t\t\tif (p.test(s)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/StringEntity.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common;\n\nimport java.util.Objects;\n\n/**\n * 这个对象是不可修改的\n */\npublic final class StringEntity<T> {\n\tprivate final String key;\n\tprivate final T value;\n\n\tprivate StringEntity(String key, T value) {\n\t\tthis.key = Objects.requireNonNull(key);\n\t\tthis.value = value;\n\t}\n\n\tpublic static <T> StringEntity<T> create(String key, T value) {\n\t\treturn new StringEntity<>(key, value);\n\t}\n\n\tpublic String key() {\n\t\treturn key;\n\t}\n\n\tpublic T value() {\n\t\treturn value;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((key == null) ? 0 : key.hashCode());\n\t\tresult = prime * result + ((value == null) ? 0 : value.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tStringEntity<?> other = (StringEntity<?>) obj;\n\t\tif (key == null) {\n\t\t\tif (other.key != null)\n\t\t\t\treturn false;\n\t\t} else if (!key.equals(other.key))\n\t\t\treturn false;\n\t\tif (value == null) {\n\t\t\tif (other.value != null)\n\t\t\t\treturn false;\n\t\t} else if (!value.equals(other.value))\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"[key=\" + key + \", value=\" + value + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/action/ActInfoUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.action;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.annotation.ExcludeFromParams;\nimport org.yx.annotation.ExcludeFromResponse;\nimport org.yx.annotation.spec.ParamSpec;\nimport org.yx.annotation.spec.Specs;\nimport org.yx.bean.aop.context.CalleeNode;\nimport org.yx.common.util.S;\nimport org.yx.common.validate.FieldParameterHolder;\nimport org.yx.common.validate.FieldParameterInfo;\nimport org.yx.common.validate.ManuParameterInfo;\nimport org.yx.common.validate.ParameterInfo;\nimport org.yx.common.validate.Validators;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\nimport org.yx.util.SumkDate;\n\npublic final class ActInfoUtil {\n\n\tpublic static Object describe(Class<?> clazz, Class<? extends Annotation> exclude) {\n\t\tif (clazz.isArray()) {\n\t\t\treturn new Object[] { describe(clazz.getComponentType(), exclude) };\n\t\t}\n\t\tif (clazz.isPrimitive() || clazz.getName().startsWith(\"java.\") || clazz == SumkDate.class) {\n\t\t\treturn clazz.getSimpleName();\n\t\t}\n\t\tif (Map.class.isAssignableFrom(clazz)) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tif (Collection.class.isAssignableFrom(clazz)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (clazz.isAnnotationPresent(exclude)) {\n\n\t\t\tLogs.http().warn(\"{}被{}注解了，可能引起一些奇怪的业务反应\", clazz.getName(), exclude.getSimpleName());\n\t\t\treturn null;\n\t\t}\n\t\tMap<String, Object> map = new LinkedHashMap<>();\n\t\tField[] fs = S.bean().getFields(clazz);\n\t\tif (AppInfo.getBoolean(\"sumk.http.act.output.class\", false)) {\n\t\t\tmap.put(\"$class\", clazz.getName());\n\t\t}\n\t\tfor (Field f : fs) {\n\t\t\tif (f.isAnnotationPresent(exclude) || f.getType().isAnnotationPresent(exclude)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmap.putIfAbsent(f.getName(), describe(f.getType(), exclude));\n\t\t}\n\t\treturn map;\n\t}\n\n\tprivate static Map<String, Object> createMap(String name, CalleeNode node) {\n\t\tMap<String, Object> map = new LinkedHashMap<>();\n\t\tmap.put(\"name\", name);\n\t\tmap.put(\"class\", node.getDeclaringClass().getName());\n\t\tmap.put(\"method\", node.getMethodName());\n\t\tmap.put(\"resultType\", node.rawMethod().getGenericReturnType().getTypeName());\n\t\treturn map;\n\t}\n\n\tpublic static Map<String, Object> simpleInfoMap(String name, CalleeNode node) {\n\t\tMap<String, Object> map = createMap(name, node);\n\t\tMap<String, Object> param = new LinkedHashMap<>();\n\t\tint paramSize = node.paramLength();\n\t\tClass<?>[] paramTypes = node.getParameterTypes();\n\t\tfor (int i = 0; i < paramSize; i++) {\n\t\t\tClass<?> paramType = paramTypes[i];\n\t\t\tparam.put(node.params().getParamName(i), describe(paramType, ExcludeFromParams.class));\n\t\t}\n\t\tmap.put(\"params\", param);\n\t\tmap.put(\"result\", describe(node.getReturnType(), ExcludeFromResponse.class));\n\t\treturn map;\n\t}\n\n\tpublic static Map<String, Object> fullInfoMap(String name, CalleeNode node) {\n\t\tMap<String, Object> map = createMap(name, node);\n\t\tList<Object> list = new ArrayList<>();\n\t\tif (node.paramInfos() != null) {\n\t\t\tint paramSize = node.paramLength();\n\t\t\tClass<?>[] paramTypes = node.getParameterTypes();\n\t\t\tfor (int i = 0; i < paramSize; i++) {\n\t\t\t\tClass<?> paramType = paramTypes[i];\n\t\t\t\tParameterInfo pi = node.paramInfos().get(i);\n\t\t\t\tif (pi == null) {\n\t\t\t\t\tManuParameterInfo mp = new ManuParameterInfo();\n\t\t\t\t\tmp.setParamType(paramType);\n\t\t\t\t\tmp.setComplex(false);\n\t\t\t\t\tmp.setParamName(node.params().getParamName(i));\n\t\t\t\t\tpi = mp;\n\t\t\t\t}\n\t\t\t\tboolean supportComplex = pi == null ? false : pi.isComplex();\n\t\t\t\tParamDescript pd = fullDescribe(paramType, pi, supportComplex, ExcludeFromParams.class);\n\t\t\t\tlist.add(pd);\n\t\t\t}\n\t\t}\n\t\tmap.put(\"params\", list);\n\t\tClass<?> returnClz = node.getReturnType();\n\t\tManuParameterInfo mp = new ManuParameterInfo();\n\t\tmp.setParamType(returnClz);\n\t\tif (!returnClz.isPrimitive()) {\n\t\t\tmp.setComplex(true);\n\t\t}\n\t\tmap.put(\"result\", fullDescribe(returnClz, mp, false, ExcludeFromResponse.class));\n\t\treturn map;\n\t}\n\n\tpublic static ParamDescript fullDescribe(Class<?> clazz, ParameterInfo info, boolean supportComplex,\n\t\t\tClass<? extends Annotation> exclude) {\n\t\tif (clazz.isArray()) {\n\t\t\tParamDescript pd = fullDescribe(clazz.getComponentType(), info, supportComplex, exclude);\n\t\t\tpd.setType(pd.getType() + \"[]\");\n\t\t\tpd.setArray(true);\n\t\t\treturn pd;\n\t\t}\n\t\tParamDescript pd = new ParamDescript();\n\t\tif (!Validators.supportComplex(clazz)) {\n\t\t\treturn pd.copyFrom(info, true).setType(clazz);\n\t\t}\n\t\tpd.copyFrom(info, supportComplex).setType(clazz);\n\t\tif (clazz.isAnnotationPresent(exclude)) {\n\n\t\t\tLogs.http().warn(\"{}被{}注解了，可能引起一些奇怪的业务反应\", clazz.getName(), exclude.getSimpleName());\n\t\t\treturn null;\n\t\t}\n\t\tList<ParamDescript> list = new ArrayList<>();\n\t\tField[] fs = S.bean().getFields(clazz);\n\t\tMap<Field, FieldParameterInfo> infoMap = FieldParameterHolder.getFieldParameterMap(clazz);\n\t\tfor (Field f : fs) {\n\t\t\tif (f.isAnnotationPresent(exclude) || f.getType().isAnnotationPresent(exclude)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tParameterInfo fp = infoMap.get(f);\n\t\t\tif (fp == null) {\n\t\t\t\tParamSpec param = Specs.extractParamField(f);\n\t\t\t\tif (param != null) {\n\t\t\t\t\tfp = new FieldParameterInfo(param, f);\n\t\t\t\t} else {\n\t\t\t\t\tManuParameterInfo mp = new ManuParameterInfo();\n\t\t\t\t\tmp.setParamName(f.getName());\n\t\t\t\t\tmp.setParamType(f.getType());\n\t\t\t\t\tfp = mp;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!Validators.supportComplex(f.getType())) {\n\t\t\t\tParamDescript leaf = new ParamDescript().copyFrom(fp, supportComplex).setType(f.getType());\n\t\t\t\tlist.add(leaf);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.add(fullDescribe(f.getType(), fp, supportComplex && fp.isComplex(), exclude));\n\t\t}\n\t\tpd.setComplexFields(list);\n\t\treturn pd;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/action/ActionStatis.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.action;\n\nimport java.util.Map;\n\npublic interface ActionStatis {\n\n\tvoid visit(String name, long time, boolean success);\n\n\tMap<String, StatisItem> getAndReset();\n\n\tMap<String, StatisItem> getAll();\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/action/ActionStatisImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.action;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class ActionStatisImpl implements ActionStatis {\n\n\tprivate Map<String, StatisItem> actStatis;\n\n\tpublic ActionStatisImpl(Map<String, StatisItem> actStatis) {\n\t\tthis.actStatis = actStatis;\n\t}\n\n\tpublic ActionStatisImpl() {\n\t\tthis.actStatis = new ConcurrentHashMap<>();\n\t}\n\n\tpublic void setActStatis(Map<String, StatisItem> actStatis) {\n\t\tthis.actStatis = Objects.requireNonNull(actStatis);\n\t}\n\n\tpublic void visit(String name, long time, boolean success) {\n\t\tStatisItem statis = actStatis.get(name);\n\t\tif (statis == null) {\n\t\t\tstatis = new StatisItem(name);\n\t\t\tStatisItem tmp = actStatis.putIfAbsent(name, statis);\n\t\t\tif (tmp != null) {\n\t\t\t\tstatis = tmp;\n\t\t\t}\n\t\t}\n\t\tif (success) {\n\t\t\tstatis.successVisit(time);\n\t\t} else {\n\t\t\tstatis.failedVisit(time);\n\t\t}\n\t}\n\n\tpublic Map<String, StatisItem> getAndReset() {\n\t\tMap<String, StatisItem> tmp = this.actStatis;\n\t\tactStatis = new ConcurrentHashMap<>();\n\t\treturn tmp;\n\t}\n\n\tpublic Map<String, StatisItem> getAll() {\n\t\treturn this.actStatis;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/action/ParamDescript.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.action;\n\nimport java.util.List;\n\nimport org.yx.common.validate.ParameterInfo;\n\npublic class ParamDescript {\n\tprivate String name;\n\n\tprivate String cnName;\n\n\tprivate Boolean required;\n\n\tprivate Integer max;\n\n\tprivate Integer min;\n\n\tprivate String type;\n\tprivate String example;\n\tprivate String comment;\n\tprivate Object custom;\n\tprivate Boolean complex;\n\n\tprivate List<ParamDescript> fields;\n\tprivate Boolean array;\n\n\tpublic List<ParamDescript> getComplexFields() {\n\t\treturn fields;\n\t}\n\n\tpublic void setComplexFields(List<ParamDescript> list) {\n\t\tthis.fields = list != null && list.size() > 0 ? list : null;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getCnName() {\n\t\treturn cnName;\n\t}\n\n\tpublic void setCnName(String cnName) {\n\t\tthis.cnName = cnName;\n\t}\n\n\tpublic Boolean getRequired() {\n\t\treturn required;\n\t}\n\n\tpublic void setRequired(Boolean required) {\n\t\tthis.required = required;\n\t}\n\n\tpublic Integer getMax() {\n\t\treturn max;\n\t}\n\n\tpublic void setMax(Integer max) {\n\t\tthis.max = max;\n\t}\n\n\tpublic Integer getMin() {\n\t\treturn min;\n\t}\n\n\tpublic void setMin(Integer min) {\n\t\tthis.min = min;\n\t}\n\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic ParamDescript setType(String type) {\n\t\tthis.type = type;\n\t\treturn this;\n\t}\n\n\tpublic ParamDescript setType(Class<?> type) {\n\t\tString subfix = \"\";\n\t\twhile (type.isArray()) {\n\t\t\tsubfix += \"[]\";\n\t\t\ttype = type.getComponentType();\n\t\t}\n\t\tthis.type = type.getName() + subfix;\n\t\treturn this;\n\t}\n\n\tpublic String getExample() {\n\t\treturn example;\n\t}\n\n\tpublic void setExample(String example) {\n\t\tthis.example = example;\n\t}\n\n\tpublic String getComment() {\n\t\treturn comment;\n\t}\n\n\tpublic void setComment(String comment) {\n\t\tthis.comment = comment;\n\t}\n\n\tpublic Object getCustom() {\n\t\treturn custom;\n\t}\n\n\tpublic void setCustom(String custom) {\n\t\tthis.custom = custom;\n\t}\n\n\tpublic Boolean getComplex() {\n\t\treturn complex;\n\t}\n\n\tpublic void setComplex(Boolean complex) {\n\t\tthis.complex = complex;\n\t}\n\n\tpublic Boolean getArray() {\n\t\treturn array;\n\t}\n\n\tpublic void setArray(Boolean array) {\n\t\tthis.array = array;\n\t}\n\n\tpublic ParamDescript copyFrom(ParameterInfo info, boolean supportComplex) {\n\t\tif (info == null) {\n\t\t\treturn this;\n\t\t}\n\t\tthis.cnName = getValue(info.getCnName());\n\t\tthis.name = getValue(info.getParamName());\n\t\tthis.comment = getValue(info.comment());\n\t\tthis.custom = info.custom();\n\t\tthis.example = getValue(info.example());\n\t\tthis.required = supportComplex && info.isRequired() ? true : null;\n\t\tthis.complex = supportComplex && info.isComplex() ? true : null;\n\t\tthis.max = supportComplex && info.getMax() >= 0 ? info.getMax() : null;\n\t\tthis.min = supportComplex && info.getMin() >= 0 ? info.getMin() : null;\n\t\treturn this;\n\t}\n\n\tprivate String getValue(String v) {\n\t\treturn v == null || v.isEmpty() ? null : v;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/action/StatisItem.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.action;\n\nimport java.util.Objects;\n\npublic class StatisItem {\n\tprivate final String name;\n\tprivate int successCount;\n\tprivate long successTime;\n\tprivate int failedCount;\n\tprivate long failedTime;\n\n\tpublic StatisItem(String name) {\n\t\tthis.name = Objects.requireNonNull(name);\n\t}\n\n\tpublic void successVisit(long t) {\n\t\tsuccessCount++;\n\t\tsuccessTime += t;\n\t}\n\n\tpublic void failedVisit(long t) {\n\t\tfailedCount++;\n\t\tfailedTime += t;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn name + \": count=\" + getSuccessCount() + \", time=\" + getSuccessTime() + \", failedCount=\"\n\t\t\t\t+ getFailedCount() + \", failedTime=\" + getFailedTime();\n\t}\n\n\tpublic String toSimpleString() {\n\t\tlong c = getSuccessCount();\n\t\tlong t = getSuccessTime();\n\t\tdouble avg = c == 0 ? 0 : t * 1d / c;\n\t\treturn String.join(\"   \", name, String.valueOf(c), String.valueOf(t), String.valueOf(Math.round(avg)),\n\t\t\t\tString.valueOf(getFailedCount()), String.valueOf(getFailedTime()));\n\t}\n\n\tpublic static String header() {\n\t\treturn \"name  success  time  avg  failed  failedTime\";\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic long getSuccessCount() {\n\t\treturn Integer.toUnsignedLong(this.successCount);\n\t}\n\n\tpublic long getSuccessTime() {\n\t\treturn this.successTime;\n\t}\n\n\tpublic long getFailedCount() {\n\t\treturn Integer.toUnsignedLong(this.failedCount);\n\t}\n\n\tpublic long getFailedTime() {\n\t\treturn this.failedTime;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/expression/AndExpression.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.expression;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.yx.exception.SumkException;\n\npublic class AndExpression implements Predicate<Map<String, Object>> {\n\n\tprivate final Predicate<Map<String, Object>>[] exps;\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic AndExpression(Collection<Predicate<Map<String, Object>>> exps) {\n\t\tif (exps == null || exps.isEmpty()) {\n\t\t\tthrow new SumkException(3455635, \"ParamExpression列表不能为空\");\n\t\t}\n\t\tthis.exps = exps.toArray(new Predicate[exps.size()]);\n\t}\n\n\t@Override\n\tpublic boolean test(Map<String, Object> map) {\n\t\tfor (Predicate<Map<String, Object>> exp : exps) {\n\t\t\tif (!exp.test(map)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"AND\" + Arrays.toString(exps);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/expression/Expressions.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.expression;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.yx.exception.SumkException;\n\npublic class Expressions {\n\tpublic static SimpleExpression createSimpleExpression(String key, String matchType) {\n\t\tif (key == null || (key = key.trim()).isEmpty()) {\n\t\t\tthrow new SumkException(345565, \"参数有误:\" + key);\n\t\t}\n\t\tswitch (matchType) {\n\t\tcase MatchType.FALSE_BY_NULL:\n\t\t\treturn new NotNullExpression(key);\n\t\tcase MatchType.FALSE_BY_NOKEY:\n\t\t\treturn new HasKeyExpression(key);\n\t\tcase MatchType.FALSE_BY_EMPTY:\n\t\t\treturn new NotEmptyExpression(key);\n\t\tdefault:\n\t\t\tthrow new SumkException(3455651, matchType + \"类型不正确\");\n\t\t}\n\t}\n\n\tpublic static Predicate<Map<String, Object>> booleanExpression(Collection<Predicate<Map<String, Object>>> exps,\n\t\t\tboolean and) {\n\t\tif (exps == null || exps.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (exps.size() == 1) {\n\t\t\treturn exps.iterator().next();\n\t\t}\n\t\treturn and ? new AndExpression(exps) : new OrExpression(exps);\n\t}\n\n\tpublic static Predicate<Map<String, Object>> and(Collection<Predicate<Map<String, Object>>> exps) {\n\t\treturn booleanExpression(exps, true);\n\t}\n\n\tpublic static Predicate<Map<String, Object>> or(Collection<Predicate<Map<String, Object>>> exps) {\n\t\treturn booleanExpression(exps, false);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/expression/HasKeyExpression.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.expression;\n\nimport java.util.Map;\n\npublic class HasKeyExpression extends SimpleExpression {\n\n\tHasKeyExpression(String key) {\n\t\tsuper(key);\n\t}\n\n\t@Override\n\tpublic boolean test(Map<String, Object> map) {\n\t\treturn map.containsKey(key);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"HasKey:\".concat(key);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/expression/MatchType.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.expression;\n\nimport org.yx.conf.AppInfo;\n\npublic final class MatchType {\n\t/**\n\t * null为false\n\t */\n\tpublic static final String FALSE_BY_NULL = \"null\";\n\t/**\n\t * map中没有key就为false\n\t */\n\tpublic static final String FALSE_BY_NOKEY = \"nokey\";\n\t/**\n\t * 只针对String，空的时候为false\n\t */\n\tpublic static final String FALSE_BY_EMPTY = \"empty\";\n\n\tpublic static String matchTypeOrDefault(String type) {\n\t\tif (type == null || (type = type.trim()).isEmpty()) {\n\t\t\treturn AppInfo.get(\"sumk.sql.falseby\", FALSE_BY_NULL);\n\t\t}\n\t\treturn type;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/expression/NotEmptyExpression.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.expression;\n\nimport java.util.Map;\n\npublic class NotEmptyExpression extends SimpleExpression {\n\n\tNotEmptyExpression(String key) {\n\t\tsuper(key);\n\t}\n\n\t@Override\n\tpublic boolean test(Map<String, Object> map) {\n\t\tObject obj = map.get(key);\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn !\"\".equals(obj);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"NotEmpty:\".concat(key);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/expression/NotNullExpression.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.expression;\n\nimport java.util.Map;\n\npublic class NotNullExpression extends SimpleExpression {\n\n\tNotNullExpression(String key) {\n\t\tsuper(key);\n\t}\n\n\t@Override\n\tpublic boolean test(Map<String, Object> map) {\n\t\treturn map.get(key) != null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\n\t\treturn key;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/expression/OrExpression.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.expression;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.yx.exception.SumkException;\n\npublic class OrExpression implements Predicate<Map<String, Object>> {\n\n\tprivate final Predicate<Map<String, Object>>[] exps;\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic OrExpression(Collection<Predicate<Map<String, Object>>> exps) {\n\t\tif (exps == null || exps.isEmpty()) {\n\t\t\tthrow new SumkException(34554565, \"ParamExpression列表不能为空\");\n\t\t}\n\t\tthis.exps = exps.toArray(new Predicate[exps.size()]);\n\t}\n\n\t@Override\n\tpublic boolean test(Map<String, Object> map) {\n\t\tfor (Predicate<Map<String, Object>> exp : exps) {\n\t\t\tif (exp.test(map)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"OR\" + Arrays.toString(exps);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/expression/SimpleExpression.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.expression;\n\nimport java.util.Map;\nimport java.util.function.Predicate;\n\npublic abstract class SimpleExpression implements Predicate<Map<String, Object>> {\n\n\tprotected final String key;\n\n\tSimpleExpression(String key) {\n\t\tthis.key = key;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((key == null) ? 0 : key.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tSimpleExpression other = (SimpleExpression) obj;\n\t\tif (key == null) {\n\t\t\tif (other.key != null)\n\t\t\t\treturn false;\n\t\t} else if (!key.equals(other.key))\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/json/ByteArrayTypeAdapter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.json;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\n\nimport org.yx.base.sumk.UnsafeByteArrayOutputStream;\nimport org.yx.common.util.S;\n\nimport com.google.gson.TypeAdapter;\nimport com.google.gson.stream.JsonReader;\nimport com.google.gson.stream.JsonToken;\nimport com.google.gson.stream.JsonWriter;\n\npublic class ByteArrayTypeAdapter extends TypeAdapter<byte[]> {\n\n\tprivate ByteArrayTypeAdapter() {\n\t}\n\n\tpublic static final ByteArrayTypeAdapter inst = new ByteArrayTypeAdapter();\n\n\t@Override\n\tpublic void write(JsonWriter out, byte[] value) throws IOException {\n\t\tif (value == null) {\n\t\t\tout.nullValue();\n\t\t\treturn;\n\t\t}\n\t\tout.value(new String(Base64.getEncoder().encode(value), StandardCharsets.UTF_8));\n\n\t}\n\n\t@Override\n\tpublic byte[] read(JsonReader in) throws IOException {\n\t\tJsonToken token = in.peek();\n\t\tif (token == JsonToken.NULL) {\n\t\t\tin.nextNull();\n\t\t\treturn null;\n\t\t}\n\t\tif (token == JsonToken.STRING) {\n\t\t\tString s = in.nextString();\n\t\t\tif (s.isEmpty()) {\n\t\t\t\treturn new byte[0];\n\t\t\t}\n\t\t\treturn S.base64().decode(s.getBytes(StandardCharsets.UTF_8));\n\t\t}\n\t\tif (token == JsonToken.BEGIN_ARRAY) {\n\t\t\treturn rawRead(in);\n\t\t}\n\t\tthrow new IOException(token + \" is not valid byte array token\");\n\t}\n\n\tprivate byte[] rawRead(JsonReader in) throws IOException {\n\t\tUnsafeByteArrayOutputStream out = new UnsafeByteArrayOutputStream(128);\n\t\tin.beginArray();\n\t\twhile (in.hasNext()) {\n\t\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\t\tin.nextNull();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout.write(in.nextInt());\n\t\t}\n\t\tin.endArray();\n\t\tout.flush();\n\t\tout.close();\n\t\treturn out.toByteArray();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/json/GsonHelper.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.json;\n\nimport java.util.Date;\n\nimport org.yx.base.date.DateAdapters;\nimport org.yx.base.date.DateTimeTypeAdapter;\nimport org.yx.conf.AppInfo;\nimport org.yx.util.StringUtil;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.LongSerializationPolicy;\n\npublic final class GsonHelper {\n\n\tpublic static GsonBuilder builder(String module) {\n\n\t\tDateTimeTypeAdapter da = new DateTimeTypeAdapter();\n\t\tString format = AppInfo.get(\"sumk.gson.date.format\");\n\t\tif (StringUtil.isNotEmpty(format)) {\n\t\t\tda.setDateFormat(format);\n\t\t}\n\n\t\tGsonBuilder gb = new GsonBuilder().registerTypeAdapter(Date.class, da);\n\n\t\tif (AppInfo.getBoolean(\"sumk.gson.disableHtmlEscaping\", true)) {\n\t\t\tgb.disableHtmlEscaping();\n\t\t}\n\t\tif (AppInfo.getBoolean(\"sumk.gson.shownull\", false)) {\n\t\t\tgb.serializeNulls();\n\t\t}\n\t\tif (AppInfo.getBoolean(\"sumk.gson.disableInnerClassSerialization\", false)) {\n\t\t\tgb.disableInnerClassSerialization();\n\t\t}\n\t\tif (AppInfo.getBoolean(\"sumk.gson.generateNonExecutableJson\", false)) {\n\t\t\tgb.generateNonExecutableJson();\n\t\t}\n\t\tif (AppInfo.getBoolean(\"sumk.gson.serializeSpecialFloatingPointValues\", false)) {\n\t\t\tgb.serializeSpecialFloatingPointValues();\n\t\t}\n\n\t\tif (AppInfo.getBoolean(\"sumk.gson.longSerialize2String\", false)) {\n\t\t\tgb.setLongSerializationPolicy(LongSerializationPolicy.STRING);\n\t\t}\n\n\t\tif (AppInfo.getBoolean(\"sumk.gson.prettyPrinting\", false)) {\n\t\t\tgb.setPrettyPrinting();\n\t\t}\n\t\tif (AppInfo.getBoolean(\"sumk.gson.date.adaper\", true)) {\n\t\t\tDateAdapters.registerAll(gb);\n\t\t}\n\t\tif (AppInfo.getBoolean(\"sumk.gson.pojo.optional\", true)) {\n\t\t\tgb.registerTypeAdapterFactory(new ParamPojoTypeAdapterFactory());\n\t\t}\n\t\treturn gb;\n\t}\n\n\tpublic static Gson gson(String module) {\n\t\treturn builder(module).create();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/json/GsonOperator.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.json;\n\nimport java.lang.reflect.Type;\nimport java.util.Objects;\n\nimport org.yx.base.sumk.UnsafeStringWriter;\n\nimport com.google.gson.Gson;\n\npublic class GsonOperator implements JsonOperator {\n\n\tprivate Gson gson;\n\n\tpublic GsonOperator(Gson gson) {\n\t\tthis.gson = Objects.requireNonNull(gson);\n\t}\n\n\tpublic void setGson(Gson gson) {\n\t\tthis.gson = Objects.requireNonNull(gson);\n\t}\n\n\tpublic Gson getGson() {\n\t\treturn gson;\n\t}\n\n\t@Override\n\tpublic String toJson(Object obj) {\n\n\t\tUnsafeStringWriter writer = new UnsafeStringWriter(32);\n\t\tgson.toJson(obj, writer);\n\t\treturn writer.toString();\n\t}\n\n\t@Override\n\tpublic <T> T fromJson(String json, Class<T> clz) {\n\t\treturn gson.fromJson(json, clz);\n\t}\n\n\t@Override\n\tpublic <T> T fromJson(String json, Type type) {\n\t\treturn gson.fromJson(json, type);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/json/JsonOperator.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.json;\n\nimport java.lang.reflect.Type;\n\npublic interface JsonOperator {\n\n\tString toJson(Object obj);\n\n\t<T> T fromJson(String json, Class<T> clz);\n\n\t<T> T fromJson(String json, Type type);\n\n}"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/json/JsonTypes.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.json;\n\nimport java.lang.reflect.Type;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport com.google.gson.reflect.TypeToken;\n\npublic final class JsonTypes {\n\tprivate static final ConcurrentMap<String, Type> types = new ConcurrentHashMap<>();\n\n\tpublic static Type registe(String name, Type type) {\n\t\treturn types.put(name, type);\n\t}\n\n\tpublic static Type registeIfAbsent(String name, Type type) {\n\t\treturn types.putIfAbsent(name, type);\n\t}\n\n\tpublic static Type remove(String name) {\n\t\treturn types.remove(name);\n\t}\n\n\tpublic static Type get(String name) {\n\t\treturn types.get(name);\n\t}\n\n\tpublic static Set<String> keys() {\n\t\treturn new HashSet<>(types.keySet());\n\t}\n\n\tpublic static Type registe(Type type) {\n\t\treturn types.put(type.getTypeName(), TypeToken.get(type).getType());\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/json/ParamPojoTypeAdapter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.json;\n\nimport java.io.IOException;\n\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.bean.aop.asm.ParamPojo;\nimport org.yx.bean.aop.asm.MethodPojo;\nimport org.yx.exception.SumkException;\n\nimport com.google.gson.Gson;\nimport com.google.gson.TypeAdapter;\nimport com.google.gson.reflect.TypeToken;\nimport com.google.gson.stream.JsonReader;\nimport com.google.gson.stream.JsonToken;\nimport com.google.gson.stream.JsonWriter;\n\npublic class ParamPojoTypeAdapter<T extends ParamPojo> extends TypeAdapter<T> {\n\t@NotNull\n\tprivate final Gson gson;\n\n\t@NotNull\n\tprivate final MethodPojo info;\n\n\tpublic ParamPojoTypeAdapter(Gson gson, MethodPojo info) {\n\t\tthis.info = info;\n\t\tthis.gson = gson;\n\t}\n\n\t@Override\n\tpublic T read(JsonReader in) throws IOException {\n\t\tif (in.peek() == JsonToken.NULL) {\n\t\t\tin.nextNull();\n\t\t\treturn null;\n\t\t}\n\n\t\tString name = null;\n\t\ttry {\n\t\t\tT pojo = info.createEmptyParamObj();\n\t\t\tObject[] objs = new Object[info.paramLength()];\n\t\t\tin.beginObject();\n\t\t\twhile (in.hasNext()) {\n\t\t\t\tname = in.nextName();\n\t\t\t\tint index = info.getIndex(name);\n\t\t\t\tif (index < 0) {\n\t\t\t\t\tin.skipValue();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tobjs[index] = gson.getAdapter(TypeToken.get(info.getParamType(index))).read(in);\n\t\t\t}\n\t\t\tin.endObject();\n\t\t\tpojo.setParams(objs);\n\t\t\treturn pojo;\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(34534234, info.paramClz().getSimpleName() + \"解析\" + name + \"字段出错：\" + e.getMessage(),\n\t\t\t\t\te);\n\t\t}\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\t@Override\n\tpublic void write(JsonWriter out, ParamPojo pojo) throws IOException {\n\t\tif (pojo == null) {\n\t\t\tout.nullValue();\n\t\t\treturn;\n\t\t}\n\n\t\tout.beginObject();\n\t\tObject[] objs = pojo.params();\n\t\tint len = info.paramLength();\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\tout.name(info.getParamName(i));\n\t\t\tTypeAdapter adapter = gson.getAdapter(TypeToken.get(info.getParamType(i)));\n\t\t\tadapter.write(out, objs[i]);\n\t\t}\n\n\t\tout.endObject();\n\t}\n}"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/json/ParamPojoTypeAdapterFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.json;\n\nimport org.yx.bean.aop.asm.ParamPojo;\nimport org.yx.bean.aop.asm.ParamPojos;\nimport org.yx.bean.aop.asm.MethodPojo;\nimport org.yx.log.Logs;\n\nimport com.google.gson.Gson;\nimport com.google.gson.TypeAdapter;\nimport com.google.gson.TypeAdapterFactory;\nimport com.google.gson.reflect.TypeToken;\n\npublic class ParamPojoTypeAdapterFactory implements TypeAdapterFactory {\n\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\t@Override\n\tpublic <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {\n\t\tif (!ParamPojo.class.isAssignableFrom(type.getRawType())) {\n\t\t\treturn null;\n\t\t}\n\t\tClass<T> clz = (Class<T>) type.getRawType();\n\t\tMethodPojo en = ParamPojos.get((Class<? extends ParamPojo>) clz);\n\t\tif (en == null) {\n\t\t\tLogs.system().info(\"{} cannot found ParamPojoInfo\", clz.getSimpleName());\n\t\t\treturn null;\n\t\t}\n\t\tif (Logs.system().isDebugEnabled()) {\n\t\t\tLogs.system().debug(\"add ParamPojoTypeAdapter for {}\", clz.getSimpleName());\n\t\t}\n\t\treturn new ParamPojoTypeAdapter(gson, en);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/json/ServerJsonExclusionStrategy.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.json;\n\nimport java.lang.annotation.Annotation;\n\nimport org.yx.annotation.ExcludeFromParams;\nimport org.yx.annotation.ExcludeFromResponse;\n\nimport com.google.gson.ExclusionStrategy;\nimport com.google.gson.FieldAttributes;\nimport com.google.gson.GsonBuilder;\n\npublic class ServerJsonExclusionStrategy implements ExclusionStrategy {\n\n\tprivate final Class<? extends Annotation> annotation;\n\n\tpublic ServerJsonExclusionStrategy(Class<? extends Annotation> excludeAnnotation) {\n\t\tthis.annotation = excludeAnnotation;\n\t}\n\n\t@Override\n\tpublic boolean shouldSkipField(FieldAttributes f) {\n\t\treturn f.getAnnotation(annotation) != null;\n\t}\n\n\t@Override\n\tpublic boolean shouldSkipClass(Class<?> clazz) {\n\t\treturn clazz.getAnnotation(annotation) != null;\n\t}\n\n\tpublic static GsonBuilder addServerExclusionStrategy(GsonBuilder gb) {\n\t\treturn gb.addSerializationExclusionStrategy(new ServerJsonExclusionStrategy(ExcludeFromResponse.class))\n\t\t\t\t.addDeserializationExclusionStrategy(new ServerJsonExclusionStrategy(ExcludeFromParams.class));\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/listener/ConcurrentSumkListener.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.listener;\n\nimport java.util.List;\nimport java.util.concurrent.Executor;\n\nimport org.yx.base.context.ActionContext;\nimport org.yx.common.util.S;\n\npublic abstract class ConcurrentSumkListener implements SumkListener {\n\n\tprivate final Executor executor;\n\n\tpublic ConcurrentSumkListener() {\n\t\tExecutor ex = this.createExecutor();\n\t\tthis.executor = ex == null ? S.executor() : ex;\n\t}\n\n\t@Override\n\tpublic void listenBatch(List<?> events) throws Exception {\n\t\texecutor.execute(ActionContext.wrapExecutable(() -> this.asyncListenBatch(events)));\n\t}\n\n\t@Override\n\tpublic void listen(Object event) throws Exception {\n\t\texecutor.execute(ActionContext.wrapExecutable(() -> this.asyncListen(event)));\n\t}\n\n\tpublic Executor executor() {\n\t\treturn this.executor;\n\t}\n\n\t/**\n\t * 创建线程池，只在初始化的时候调用一次。 如果想要单线程排队执行，使用Executors.newSingleThreadExecutor()就可以\n\t * \n\t * @return 线程池，如果返回null就使用系统默认的线程池\n\t */\n\tprotected Executor createExecutor() {\n\t\treturn null;\n\t}\n\n\tprotected void asyncListenBatch(List<?> events) throws Exception {\n\t\tfor (Object event : events) {\n\t\t\tthis.listen(event);\n\t\t}\n\t}\n\n\tprotected abstract void asyncListen(Object event) throws Exception;\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/listener/EventBus.java",
    "content": "package org.yx.common.listener;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.Executor;\n\nimport org.yx.base.context.ActionContext;\nimport org.yx.log.Log;\nimport org.yx.util.CollectionUtil;\n\n/**\n * 它的bean统一有框架创建\n */\npublic class EventBus {\n\n\tprivate final SumkListener[] listeners;\n\tprivate final Executor executor;\n\n\tpublic EventBus(List<SumkListener> list, Executor executor) {\n\t\tlist.sort(null);\n\t\tthis.listeners = list.toArray(new SumkListener[list.size()]);\n\t\tthis.executor = Objects.requireNonNull(executor);\n\t}\n\n\tpublic void publishBatch(List<?> events) {\n\t\tfor (SumkListener l : listeners) {\n\t\t\ttry {\n\t\t\t\tl.listenBatch(events);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tLog.get(\"sumk.event\").error(l + \"批量执行出错,\" + e.getLocalizedMessage(), e);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void publish(Object event) {\n\t\tfor (SumkListener l : listeners) {\n\t\t\ttry {\n\t\t\t\tl.listen(event);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.get(\"sumk.event\").error(l + \"执行出错,\" + e.getLocalizedMessage(), e);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void asyncPublishBatch(List<?> events) {\n\t\texecutor.execute(ActionContext.wrapExecutable(() -> this.publishBatch(events)));\n\t}\n\n\tpublic void asyncPublish(Object event) {\n\t\texecutor.execute(ActionContext.wrapExecutable(() -> this.publish(event)));\n\t}\n\n\tpublic List<SumkListener> listeners() {\n\t\treturn CollectionUtil.unmodifyList(listeners);\n\t}\n\n\tpublic Executor executor() {\n\t\treturn this.executor;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/listener/EventBusFactory.java",
    "content": "package org.yx.common.listener;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Executor;\n\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.Priority;\nimport org.yx.bean.FactoryBean;\nimport org.yx.bean.IOC;\nimport org.yx.bean.NamedBean;\nimport org.yx.log.Log;\nimport org.yx.main.SumkServer;\n\n@Bean\n@Priority(20000)\npublic class EventBusFactory implements FactoryBean {\n\n\t@Override\n\tpublic Collection<?> beans() {\n\t\tMap<String, List<SumkListener>> map = new HashMap<>();\n\t\tfor (SumkListener listener : IOC.getBeans(SumkListener.class)) {\n\t\t\tfor (String type : listener.acceptType()) {\n\t\t\t\tif (type == null || (type = type.trim()).isEmpty() || IOC.get(type, EventBus.class) != null) {\n\t\t\t\t\tLog.get(\"sumk.listen\").error(\"{}的EventBus已经存在\", type);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tList<SumkListener> list = map.get(type);\n\t\t\t\tif (list == null) {\n\t\t\t\t\tlist = new ArrayList<>();\n\t\t\t\t\tmap.put(type, list);\n\t\t\t\t}\n\t\t\t\tif (!list.contains(listener)) {\n\t\t\t\t\tlist.add(listener);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tList<NamedBean> beans = new ArrayList<>();\n\t\tfor (String type : map.keySet()) {\n\t\t\tExecutor ex = SumkServer.getExecutor(\"sumk.event.executor.\" + type);\n\t\t\tEventBus bus = new EventBus(map.get(type), ex);\n\t\t\tbeans.add(new NamedBean(type, bus));\n\t\t}\n\t\treturn beans;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/listener/SumkListener.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.listener;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.yx.base.Ordered;\n\n/**\n * 异步执行使用ConcurrentSumkListener\n */\npublic interface SumkListener extends Ordered {\n\n\tCollection<String> acceptType();\n\n\tdefault void listenBatch(List<?> events) throws Exception {\n\t\tfor (Object event : events) {\n\t\t\tthis.listen(event);\n\t\t}\n\t}\n\n\tvoid listen(Object event) throws Exception;\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/locale/I18n.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.locale;\n\nimport java.util.Locale;\nimport java.util.Objects;\n\n/**\n * 一般用于国际化或提示语\n */\npublic final class I18n {\n\n\tprivate static I18nMessageProvider provider = new I18nMessageProviderImpl();\n\n\tstatic I18nMessageProvider getProvider() {\n\t\treturn provider;\n\t}\n\n\tstatic void setProvider(I18nMessageProvider provider) {\n\t\tI18n.provider = Objects.requireNonNull(provider);\n\t}\n\n\tpublic static String get(String orignName, String defaultTemplate, Object... params) {\n\t\treturn provider.get(orignName, defaultTemplate, params);\n\t}\n\n\tpublic static String getInLocale(Locale locale, String orignName, String defaultTemplate, Object... params) {\n\t\treturn provider.getInLocale(locale, orignName, defaultTemplate, params);\n\t}\n\n\tpublic static void setCurrentLocale(Locale locale) {\n\t\tprovider.setCurrentLocale(locale);\n\t}\n\n\tpublic static void clearCurrentLocale() {\n\t\tprovider.clearCurrentLocale();\n\t}\n\n\tpublic static Locale getCurrentLocale() {\n\t\treturn provider.getCurrentLocale();\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/locale/I18nBuilder.java",
    "content": "package org.yx.common.locale;\n\npublic class I18nBuilder {\n\tpublic static I18nMessageProvider getProvider() {\n\t\treturn I18n.getProvider();\n\t}\n\n\tstatic void setProvider(I18nMessageProvider provider) {\n\t\tI18n.setProvider(provider);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/locale/I18nMessageProvider.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.locale;\n\nimport java.util.Locale;\n\npublic interface I18nMessageProvider {\n\n\tvoid setCurrentLocale(Locale locale);\n\n\tvoid clearCurrentLocale();\n\n\tLocale getCurrentLocale();\n\n\tString get(String orignName, String defaultTemplate, Object... params);\n\n\tString getInLocale(Locale locale, String orignName, String defaultTemplate, Object... params);\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/locale/I18nMessageProviderImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.locale;\n\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.util.StringUtil;\n\n/**\n * 一般用于国际化或提示语\n */\npublic class I18nMessageProviderImpl implements I18nMessageProvider {\n\n\tprivate static final ThreadLocal<Locale> CURRENT_LOCALE = new ThreadLocal<>();\n\n\t@Override\n\tpublic String get(String orignName, String defaultTemplate, Object... params) {\n\t\tLocale locale = this.getCurrentLocale();\n\t\tif (locale == null) {\n\t\t\tlocale = Locale.getDefault();\n\t\t}\n\t\treturn this.getInLocale(locale, orignName, defaultTemplate, params);\n\t}\n\n\t@Override\n\tpublic String getInLocale(Locale locale, String orignName, String defaultTemplate, Object... params) {\n\t\tList<String> names = localedNames(orignName, locale);\n\t\tString template = null;\n\t\tfor (String localeName : names) {\n\t\t\ttemplate = AppInfo.get(localeName);\n\t\t\tif (StringUtil.isNotEmpty(template)) {\n\t\t\t\treturn buildMessage(template, locale, params);\n\t\t\t}\n\t\t}\n\t\treturn buildMessage(defaultTemplate, locale, params);\n\t}\n\n\tpublic List<String> localedNames(String name, Locale locale) {\n\t\tif (locale == null) {\n\t\t\treturn Collections.singletonList(name);\n\t\t}\n\t\tList<String> result = new ArrayList<>(3);\n\t\tresult.add(name);\n\t\tString language = locale.getLanguage();\n\t\tString country = locale.getCountry();\n\t\tString variant = locale.getVariant();\n\t\tStringBuilder temp = new StringBuilder();\n\n\t\tif (language.length() > 0) {\n\t\t\ttemp.append(language);\n\t\t\tresult.add(0, buildName(name, temp));\n\t\t}\n\t\tif (country.length() > 0) {\n\t\t\tif (temp.length() > 0) {\n\t\t\t\ttemp.append('_');\n\t\t\t}\n\t\t\ttemp.append(country);\n\t\t\tresult.add(0, buildName(name, temp));\n\t\t}\n\n\t\tif (variant.length() > 0 && (language.length() > 0 || country.length() > 0)) {\n\t\t\tif (temp.length() > 0) {\n\t\t\t\ttemp.append('_');\n\t\t\t}\n\t\t\ttemp.append(variant);\n\t\t\tresult.add(0, buildName(name, temp));\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprotected String buildName(String name, StringBuilder temp) {\n\t\tStringBuilder sb = new StringBuilder(temp);\n\t\tif (sb.length() > 0) {\n\t\t\tsb.append('.');\n\t\t}\n\t\treturn sb.append(name).toString();\n\t}\n\n\tprotected String buildMessage(String template, Locale locale, Object... params) {\n\n\t\tMessageFormat formater = new MessageFormat(template);\n\t\tif (locale != null) {\n\t\t\tformater.setLocale(locale);\n\t\t}\n\t\treturn formater.format(params);\n\t}\n\n\t@Override\n\tpublic void setCurrentLocale(Locale locale) {\n\t\tCURRENT_LOCALE.set(locale);\n\t}\n\n\t@Override\n\tpublic void clearCurrentLocale() {\n\t\tCURRENT_LOCALE.remove();\n\t}\n\n\t@Override\n\tpublic Locale getCurrentLocale() {\n\t\treturn CURRENT_LOCALE.get();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/lock/Lock.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.lock;\n\n/**\n * 分布式锁的钥匙。lock方法要从S.lock进入\n * \n * @author 游夏\n *\n */\npublic interface Lock extends AutoCloseable {\n\n\tString getId();\n\n\tString getValue();\n\n\tvoid unlock();\n\n\t@Override\n\tdefault void close() {\n\t\tthis.unlock();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/lock/Locked.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.lock;\n\npublic final class Locked implements Lock {\n\tprivate final String id;\n\tprivate final String value;\n\n\tpublic Locked(String id, String value) {\n\t\tthis.id = id;\n\t\tthis.value = value;\n\t}\n\n\t@Override\n\tpublic String getId() {\n\t\treturn this.id;\n\t}\n\n\t@Override\n\tpublic String getValue() {\n\t\treturn value;\n\t}\n\n\t@Override\n\tpublic void unlock() {\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/lock/Locker.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.lock;\n\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.yx.base.context.AppContext;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.main.SumkServer;\nimport org.yx.redis.Redis;\nimport org.yx.redis.RedisPool;\nimport org.yx.util.IOUtil;\nimport org.yx.util.Task;\n\npublic final class Locker {\n\tprivate static final int REDIS_LEN = 16;\n\tprivate static final String[] nodeKey = new String[REDIS_LEN];\n\n\tpublic static synchronized void init() {\n\t\tif (nodeKey[0] != null) {\n\t\t\treturn;\n\t\t}\n\t\tfor (int i = 0; i < REDIS_LEN; i++) {\n\t\t\tnodeKey[i] = \"lock_\" + i;\n\t\t}\n\t\tensureScriptRunner.run();\n\t\tTask.scheduleAtFixedRate(ensureScriptRunner, AppInfo.getLong(\"sumk.lock.schedule.delay\", 1000L * 600),\n\t\t\t\tAppInfo.getLong(\"sumk.lock.schedule.delay\", 1000L * 600));\n\t}\n\n\tprivate Locker() {\n\t}\n\n\tpublic static final Locker inst = new Locker();\n\n\tstatic Redis redis(String id) {\n\t\tint index = id.hashCode() & (REDIS_LEN - 1);\n\t\tRedis redis = RedisPool.get(nodeKey[index]);\n\t\tif (redis == null) {\n\t\t\tthrow new SumkException(8295462, \"SLock must use in redis environment\");\n\t\t}\n\t\treturn redis.mute();\n\t}\n\n\tprivate static final ThreadLocal<List<SLock>> locks = new ThreadLocal<List<SLock>>() {\n\t\t@Override\n\t\tprotected List<SLock> initialValue() {\n\t\t\treturn new ArrayList<>(2);\n\t\t}\n\t};\n\n\tprivate static SLock getLock(final Lock key) {\n\t\tList<SLock> list = locks.get();\n\t\tif (list == null || list.isEmpty() || key == null) {\n\t\t\treturn null;\n\t\t}\n\t\tlist = new ArrayList<>(list);\n\t\tfor (SLock lock : list) {\n\t\t\tif (!lock.isEnable()) {\n\t\t\t\tLog.get(\"sumk.lock\").warn(\"remove unable lock: {}\", lock);\n\t\t\t\tlock.unlock();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (lock.getId().equals(key.getId())) {\n\t\t\t\treturn lock;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic int releaseLocalLocks() {\n\t\tList<SLock> list = locks.get();\n\t\tlist = new ArrayList<>(list);\n\t\tfor (SLock lock : list) {\n\t\t\tlock.unlock();\n\t\t}\n\t\tlocks.remove();\n\t\treturn list.size();\n\t}\n\n\tvoid remove(final Lock lock) {\n\t\tif (lock == null || lock instanceof Locked) {\n\t\t\treturn;\n\t\t}\n\t\tList<SLock> list = locks.get();\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tListIterator<SLock> it = list.listIterator(list.size());\n\t\twhile (it.hasPrevious()) {\n\t\t\tSLock lock2 = it.previous();\n\t\t\tif (lock2.getId().equals(lock.getId())) {\n\t\t\t\tit.remove();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * \n\t * @param name        要被锁的对象\n\t * @param maxWaitTime 获取锁的最大时间，单位ms\n\t * @param maxLockTime 最大的锁住时间，单位ms\n\t * @return Key对象,或者null\n\t */\n\tpublic Lock tryLock(String name, int maxWaitTime, int maxLockTime) {\n\t\tSLock lock = SLock.create(name, maxLockTime);\n\t\treturn tryLock(lock, maxWaitTime);\n\t}\n\n\t/**\n\t * \n\t * @param name        要被锁的对象\n\t * @param maxWaitTime 获取锁的最大时间，单位ms。如果只想尝试一次，可以传0\n\t * @return 锁的钥匙，获取不到锁就返回null\n\t */\n\tpublic Lock tryLock(String name, int maxWaitTime) {\n\t\tSLock lock = SLock.create(name);\n\t\treturn tryLock(lock, maxWaitTime);\n\t}\n\n\tpublic Lock lock(String name) {\n\t\treturn tryLock(name, Integer.MAX_VALUE);\n\t}\n\n\t/**\n\t * 尝试加锁，如果锁定失败，就返回null\n\t * \n\t * @param lock        锁对象\n\t * @param maxWaitTime 获取锁的最大时间，单位ms。无论值为多少，都至少会尝试一次\n\t * @return Key对象,或者null\n\t */\n\tpublic Lock tryLock(SLock lock, int maxWaitTime) {\n\t\tLock old = getLock(lock);\n\t\tif (old != null) {\n\t\t\treturn new Locked(old.getId(), old.getValue());\n\t\t}\n\t\tif (lock.lock(maxWaitTime)) {\n\t\t\tlocks.get().add(lock);\n\t\t\treturn lock;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic boolean isLockedNow(Lock lock) {\n\t\treturn Objects.equals(lock.getValue(), Locker.redis(lock.getId()).get(lock.getId()));\n\t}\n\n\t/**\n\t * 重置锁超时时间，对重进入的锁也适用\n\t * \n\t * @param lock 锁\n\t * @param mils 重置锁的过期时间为当前值\n\t * @return 如果锁已经被释放，或其它未知原因，就返回false\n\t */\n\tpublic boolean resetExpiredTime(Lock lock, int mils) {\n\t\tSLock slock = getLock(lock);\n\t\tif (slock == null || !isLockedNow(slock)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlong endTime = System.currentTimeMillis() + mils;\n\t\tLong resp = Locker.redis(slock.getId()).pexpireAt(slock.getId(), endTime);\n\t\tif (resp != null && resp.longValue() > 0) {\n\t\t\tslock.resetEndTime(endTime);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static final Runnable ensureScriptRunner = () -> {\n\t\ttry {\n\t\t\tInputStream in = SumkServer.class.getClassLoader().getResourceAsStream(\"META-INF/lua_del\");\n\t\t\tString script = new String(IOUtil.readAllBytes(in, true), AppInfo.UTF8);\n\t\t\tscript = script.replace('\\r', '\\n').replace(\"\\n\\n\", \"\\n\");\n\t\t\tSet<Redis> set = new HashSet<>();\n\t\t\tfor (String key : nodeKey) {\n\t\t\t\tRedis redis = RedisPool.get(key);\n\t\t\t\tif (redis == null || !set.add(redis)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog.get(\"sumk.lock\").debug(\"init lock script\", redis);\n\n\t\t\t\tredis.scriptLoad(script);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLog.get(\"sumk.lock\").error(\"Lock init failed. Maybe you need restart!!!\");\n\t\t\tLog.printStack(\"sumk.lock\", e);\n\t\t\tif (AppInfo.getBoolean(\"sumk.shutdown.if.lock.failed\", false)) {\n\t\t\t\tAppContext.startFailed();\n\t\t\t}\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/lock/SLock.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.lock;\n\nimport org.yx.common.util.kit.Asserts;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Log;\nimport org.yx.util.UUIDSeed;\n\npublic final class SLock implements Lock {\n\tprivate static final String SHA = \"fc8341f94e518c9868148c2b8fc7cef25ec6fa85\";\n\tprivate static final long CLOSED = -1;\n\tprivate final String id;\n\tprivate final String value;\n\tprivate final int maxLockTime;\n\tprivate final int intervalTime;\n\n\tprivate long endTime;\n\tprivate long startNS;\n\n\tpublic SLock(String keyId, String value, int maxLockTime, int intervalTime) {\n\t\tAsserts.requireTrue(keyId != null && (keyId = keyId.trim()).length() > 0, \"lock name cannot be empty\");\n\t\tAsserts.requireTrue(intervalTime > 0 && maxLockTime > 0 && value != null && value.length() > 0,\n\t\t\t\t\"lock param is not valid\");\n\t\tthis.id = keyId;\n\t\tthis.value = value;\n\t\tthis.maxLockTime = maxLockTime;\n\t\tthis.intervalTime = intervalTime;\n\t}\n\n\tpublic String getId() {\n\t\treturn id;\n\t}\n\n\tpublic String getValue() {\n\t\treturn value;\n\t}\n\n\tpublic static SLock create(String name, int maxLockTime, int intervalTime) {\n\t\treturn new SLock(name, UUIDSeed.seq(), maxLockTime, intervalTime);\n\t}\n\n\tpublic static SLock create(String name) {\n\t\treturn create(name, AppInfo.getInt(\"sumk.lock.maxLockTime\", 60000));\n\t}\n\n\tpublic static SLock create(String name, int maxLockTime) {\n\t\treturn create(name, maxLockTime, AppInfo.getInt(\"sumk.lock.intervalTime\", 50));\n\t}\n\n\tboolean tryLock() {\n\t\tthis.startNS = System.nanoTime();\n\t\tString ret = Locker.redis(id).set(id, value, \"NX\", \"PX\", maxLockTime);\n\t\tif (ret == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn ret.equalsIgnoreCase(\"OK\") || ret.equals(\"1\");\n\t}\n\n\tboolean lock(final int maxWaitTime) {\n\t\tlong now = System.currentTimeMillis();\n\t\tthis.endTime = now + this.maxLockTime;\n\t\tif (this.endTime < 1) {\n\t\t\tthis.endTime = 1;\n\t\t}\n\t\tlong waitEndTime = now + maxWaitTime;\n\t\twhile (true) {\n\t\t\tif (tryLock()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tlong left = waitEndTime - System.currentTimeMillis();\n\t\t\tif (left <= 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tlong sleepTime = Math.min(left, (long) this.intervalTime);\n\t\t\ttry {\n\t\t\t\tLog.get(\"sumk.lock\").debug(\"locked failed: {}={},sleep {}ms\", id, value, sleepTime);\n\t\t\t\tThread.sleep(sleepTime);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid resetEndTime(long endTime) {\n\t\tthis.endTime = endTime;\n\t}\n\n\tboolean isEnable() {\n\t\tlong end = this.endTime;\n\t\treturn end > 0 && System.currentTimeMillis() < end;\n\t}\n\n\t@Override\n\tpublic void unlock() {\n\t\tif (this.endTime == CLOSED) {\n\t\t\treturn;\n\t\t}\n\t\tLocker.redis(id).evalsha(SHA, 1, id, value);\n\t\tthis.endTime = CLOSED;\n\t\tLocker.inst.remove(this);\n\t\tLog.get(\"sumk.lock\").info(\"unlock {}:{},spent {}ns\", id, value, System.nanoTime() - this.startNS);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn id + \"=\" + value + \" : \" + this.endTime;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/monitor/MessageProvider.java",
    "content": "package org.yx.common.monitor;\n\npublic interface MessageProvider {\n\n\tObject get(String type, String key, Object param);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/monitor/Monitors.java",
    "content": "package org.yx.common.monitor;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Monitors {\n\tprivate static final List<MessageProvider> providers = new ArrayList<>(5);\n\tpublic static final String BLANK = \"  \";\n\n\tpublic static synchronized void add(MessageProvider provider) {\n\t\tif (!providers.contains(provider)) {\n\t\t\tproviders.add(provider);\n\t\t}\n\t}\n\n\tpublic static Object getMessage(String type, String key, Object param) {\n\t\tfor (MessageProvider p : providers) {\n\t\t\tObject msg = p.get(type, key, param);\n\t\t\tif (msg != null) {\n\t\t\t\treturn msg;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/route/AbstractWeightedServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.route;\n\nimport java.util.Objects;\n\npublic abstract class AbstractWeightedServer<T> implements WeightedServer<T> {\n\n\tprotected final T source;\n\n\tprivate int weight = 1;\n\n\tpublic AbstractWeightedServer(T source) {\n\t\tthis.source = Objects.requireNonNull(source);\n\t}\n\n\t@Override\n\tpublic int getWeight() {\n\t\treturn weight;\n\t}\n\n\tpublic void setWeight(int weight) {\n\t\tthis.weight = weight > 0 ? weight : 0;\n\t}\n\n\t@Override\n\tpublic T getSource() {\n\t\treturn this.source;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((source == null) ? 0 : source.hashCode());\n\t\tresult = prime * result + weight;\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tAbstractWeightedServer<?> other = (AbstractWeightedServer<?>) obj;\n\t\tif (source == null) {\n\t\t\tif (other.source != null)\n\t\t\t\treturn false;\n\t\t} else if (!source.equals(other.source))\n\t\t\treturn false;\n\t\tif (weight != other.weight)\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int compareTo(WeightedServer<T> o) {\n\t\treturn Integer.compare(o.getWeight(), this.getWeight());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"WeightedSource [source=\" + source + \", weight=\" + weight + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/route/EmptyRouter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.route;\n\nimport java.util.Collections;\nimport java.util.List;\n\npublic class EmptyRouter<T> implements Router<T> {\n\n\t@Override\n\tpublic T select() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic List<T> allSources() {\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Override\n\tpublic List<T> aliveSources() {\n\t\treturn Collections.emptyList();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/route/Router.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.route;\n\nimport java.util.List;\n\npublic interface Router<T> {\n\n\tT select();\n\n\tList<T> allSources();\n\n\tList<T> aliveSources();\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/route/Routes.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.route;\n\nimport java.util.Collection;\n\npublic final class Routes {\n\n\tpublic static <T> Router<T> createWeightedRouter(Collection<WeightedServer<T>> servers) {\n\t\tif (servers == null || servers.isEmpty()) {\n\t\t\treturn new EmptyRouter<>();\n\t\t}\n\t\tif (servers.size() == 1) {\n\t\t\treturn new SingleRouter<>(servers.iterator().next());\n\t\t}\n\t\treturn new WeightedRouter<>(servers);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/route/SingleRouter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.route;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class SingleRouter<T> implements Router<T> {\n\n\tprivate final WeightedServer<T> server;\n\n\tpublic SingleRouter(WeightedServer<T> server) {\n\t\tthis.server = Objects.requireNonNull(server);\n\t}\n\n\t@Override\n\tpublic T select() {\n\t\tif (!this.server.isEnable()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this.server.getSource();\n\t}\n\n\t@Override\n\tpublic List<T> allSources() {\n\t\treturn Collections.singletonList(this.server.getSource());\n\t}\n\n\t@Override\n\tpublic List<T> aliveSources() {\n\t\treturn server.isEnable() ? Collections.singletonList(this.server.getSource()) : Collections.emptyList();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/route/WeightedRouter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.route;\n\nimport java.math.BigInteger;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\npublic class WeightedRouter<T> implements Router<T> {\n\n\tprotected int currentIndex = -1;\n\n\tprotected int currentWeight;\n\n\tprotected final int MAX_WEIGHT;\n\n\tprotected final int GCD_WEIGHT;\n\n\tprotected final WeightedServer<T>[] SERVERS;\n\n\tpublic WeightedRouter(Collection<WeightedServer<T>> servers) {\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tWeightedServer<T>[] ws = servers.toArray(new WeightedServer[servers.size()]);\n\t\tArrays.sort(ws);\n\t\tSERVERS = ws;\n\t\tMAX_WEIGHT = getMaxWeightForServers();\n\t\tGCD_WEIGHT = getGCDForServers();\n\t\tthis.currentWeight = this.MAX_WEIGHT;\n\t}\n\n\tprivate BigInteger gcd(BigInteger a, BigInteger b) {\n\t\treturn a.gcd(b);\n\t}\n\n\tprotected int getGCDForServers() {\n\t\tList<BigInteger> list = new ArrayList<>(SERVERS.length);\n\t\tfor (WeightedServer<T> sm : SERVERS) {\n\t\t\tint weight = sm.getWeight();\n\t\t\tif (weight <= 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.add(new BigInteger(String.valueOf(weight)));\n\t\t}\n\t\tBigInteger w = new BigInteger(\"0\");\n\t\tfor (int i = 0, len = list.size(); i < len - 1; i++) {\n\t\t\tif (w.intValue() == 0) {\n\t\t\t\tw = gcd(list.get(i), list.get(i + 1));\n\t\t\t} else {\n\t\t\t\tw = gcd(w, list.get(i + 1));\n\t\t\t}\n\t\t}\n\t\tint gcd = w.intValue();\n\t\treturn gcd > 0 ? gcd : 1;\n\t}\n\n\tprotected int getMaxWeightForServers() {\n\t\tint w = 0;\n\t\tfor (WeightedServer<T> s : SERVERS) {\n\t\t\tif (s.getWeight() > w) {\n\t\t\t\tw = s.getWeight();\n\t\t\t}\n\t\t}\n\t\treturn w;\n\t}\n\n\t@Override\n\tpublic List<T> allSources() {\n\t\tList<T> list = new ArrayList<>(SERVERS.length);\n\t\tfor (WeightedServer<T> s : this.SERVERS) {\n\t\t\tlist.add(s.getSource());\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic List<T> aliveSources() {\n\t\tList<T> list = new ArrayList<>(SERVERS.length);\n\t\tfor (WeightedServer<T> s : this.SERVERS) {\n\t\t\tif (!s.isEnable()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.add(s.getSource());\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic T select() {\n\t\tint index = 0;\n\t\tfinal int SERVER_COUNT = SERVERS.length;\n\n\t\tfor (int i = 0; i < SERVER_COUNT; i++) {\n\t\t\tindex = (currentIndex + 1) % SERVER_COUNT;\n\t\t\tcurrentIndex = index;\n\n\t\t\tif (index == 0) {\n\t\t\t\tint tempWeight = currentWeight - GCD_WEIGHT;\n\n\t\t\t\tcurrentWeight = tempWeight < 1 ? MAX_WEIGHT : tempWeight;\n\t\t\t}\n\n\t\t\tWeightedServer<T> server = SERVERS[index];\n\t\t\tif (server.getWeight() >= currentWeight) {\n\t\t\t\tif (!server.isEnable()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\treturn server.getSource();\n\t\t\t}\n\t\t}\n\n\t\tfor (int i = 0; i < SERVER_COUNT; i++) {\n\t\t\tWeightedServer<T> w = SERVERS[i % SERVER_COUNT];\n\t\t\tif (w.isEnable()) {\n\t\t\t\treturn w.getSource();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"[MAX_WEIGHT=\" + MAX_WEIGHT + \", GCD_WEIGHT=\" + GCD_WEIGHT + \", SERVER_COUNT=\" + SERVERS.length + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/route/WeightedServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.route;\n\npublic interface WeightedServer<T> extends Comparable<WeightedServer<T>> {\n\n\tint getWeight();\n\n\tvoid setWeight(int weight);\n\n\tT getSource();\n\n\tboolean isEnable();\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/sequence/AbstractSeq.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.sequence;\n\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.concurrent.atomic.AtomicIntegerArray;\n\nimport org.slf4j.Logger;\nimport org.yx.log.Log;\n\npublic abstract class AbstractSeq implements Seq {\n\n\tpublic static final long FROMMILS_V1 = 1420041600000L;\n\tpublic static final long FROMMILS_V2 = 1717171200000L;\n\tprivate static final int LOCAL_SEQ_INDEX = 64;\n\tprivate AtomicIntegerArray localSeqs = new AtomicIntegerArray(LOCAL_SEQ_INDEX + 1);\n\tprotected final long fromMils;\n\tprotected SeqCounter counter;\n\n\tpublic AbstractSeq() {\n\t\tthis(FROMMILS_V2);\n\t}\n\n\tpublic AbstractSeq(long from) {\n\t\tthis.fromMils = from;\n\t\ttry {\n\t\t\tfor (int i = 0; i < localSeqs.length(); i++) {\n\t\t\t\tlocalSeqs.set(i, ThreadLocalRandom.current().nextInt(256));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLog.get(\"sumk.seq\").error(e.getLocalizedMessage(), e);\n\t\t}\n\t}\n\n\tprotected int localHashIndex(String name) {\n\t\tif (name == null || name.isEmpty()) {\n\t\t\treturn LOCAL_SEQ_INDEX;\n\t\t}\n\t\treturn name.hashCode() & (LOCAL_SEQ_INDEX - 1);\n\t}\n\n\tprotected int localSeq(String name) {\n\t\tint hash = localHashIndex(name);\n\t\tint num = localSeqs.incrementAndGet(hash);\n\t\tif (num > 0x3FFFFFFF) {\n\t\t\tlocalSeqs.compareAndSet(hash, num, ThreadLocalRandom.current().nextInt(100));\n\t\t}\n\t\treturn num;\n\t}\n\n\tprotected int subNumber(String name) {\n\t\tif (counter != null) {\n\t\t\ttry {\n\t\t\t\treturn counter.incr(name);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLogger log = Log.get(\"sumk.seq\");\n\t\t\t\tif (log.isTraceEnabled()) {\n\t\t\t\t\tlog.trace(e.getLocalizedMessage(), e);\n\t\t\t\t} else {\n\t\t\t\t\tlog.debug(e.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tint sub = (ThreadLocalRandom.current().nextInt(0x100) << 16);\n\t\tsub |= ((int) System.nanoTime()) & 0xFF00;\n\t\treturn sub | (localSeq(name) & 0xFF);\n\n\t}\n\n\t@Override\n\tpublic void setCounter(SeqCounter counter) {\n\t\tthis.counter = counter;\n\t}\n\n\t@Override\n\tpublic SeqCounter getCounter() {\n\t\treturn this.counter;\n\t}\n\n\tprotected final long shortMills(long time) {\n\t\treturn time - fromMils;\n\t}\n\n\tprotected long fullTime(long time) {\n\t\treturn time + fromMils;\n\t}\n\n\t@Override\n\tpublic long next() {\n\t\treturn next(null);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/sequence/LongTermSeqImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.sequence;\n\nimport org.yx.util.SumkDate;\n\npublic final class LongTermSeqImpl extends AbstractSeq {\n\n\tpublic LongTermSeqImpl() {\n\t\tsuper();\n\t}\n\n\tpublic LongTermSeqImpl(long from) {\n\t\tsuper(from);\n\t}\n\n\tpublic long next(String name) {\n\t\tlong sub = subNumber(name) & 0x7FFFFFL;\n\t\treturn prefix(System.currentTimeMillis()) | sub;\n\t}\n\n\tprivate long prefix(long time) {\n\t\tlong num = shortMills(time);\n\t\tnum &= 0x1FFFFFFFFFFL;\n\t\treturn num << 23;\n\t}\n\n\tpublic long getTimeMillis(long seq) {\n\t\tlong num = seq & 0xFFFFFFFFFF800000L;\n\t\tnum >>>= 23;\n\t\treturn fullTime(num);\n\t}\n\n\t@Override\n\tpublic long low(SumkDate date) {\n\t\treturn prefix(date.getTimeInMils());\n\t}\n\n\t@Override\n\tpublic long high(SumkDate date) {\n\t\treturn prefix(date.getTimeInMils()) | 0x7FFFFF;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/sequence/Seq.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.sequence;\n\nimport org.yx.util.SumkDate;\n\npublic interface Seq {\n\n\tvoid setCounter(SeqCounter counter);\n\n\tSeqCounter getCounter();\n\n\tlong next(String name);\n\n\tlong next();\n\n\tlong getTimeMillis(long seq);\n\n\tlong low(SumkDate date);\n\n\tlong high(SumkDate date);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/sequence/SeqCounter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.sequence;\n\npublic interface SeqCounter {\n\n\tint incr(String name) throws Exception;\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/sequence/SeqHolder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.sequence;\n\nimport java.util.Objects;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Log;\n\npublic class SeqHolder {\n\tprivate static Seq createDefaultSeq() {\n\t\tint snow = AppInfo.getInt(\"sumk.seq.counter.snow\", Integer.MIN_VALUE);\n\t\tif (snow != Integer.MIN_VALUE) {\n\t\t\tSeq seq = new SeqImpl();\n\t\t\tseq.setCounter(new SnowflakeCounter(snow));\n\t\t\tLog.get(\"sumk.seq\").info(\"use snow counter\");\n\t\t\treturn seq;\n\t\t}\n\t\tint version = AppInfo.getInt(\"sumk.seq.version\", 0);\n\t\tif (version == 1) {\n\t\t\tLog.get(\"sumk.seq\").info(\"use v1.0 seq\");\n\t\t\treturn new SeqImpl(AbstractSeq.FROMMILS_V1);\n\t\t}\n\t\treturn new LongTermSeqImpl();\n\t}\n\n\tprivate static Seq inst = createDefaultSeq();\n\n\tpublic static Seq inst() {\n\t\treturn inst;\n\t}\n\n\tpublic static void setSeq(Seq seq) {\n\t\tSeqHolder.inst = Objects.requireNonNull(seq);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/sequence/SeqImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.sequence;\n\nimport org.yx.util.SumkDate;\n\npublic final class SeqImpl extends AbstractSeq {\n\n\tpublic SeqImpl() {\n\t\tsuper();\n\t}\n\n\tpublic SeqImpl(long from) {\n\t\tsuper(from);\n\t}\n\n\t@Override\n\tpublic long next(String name) {\n\n\t\tint sub = subNumber(name) & 0xFFFFFF;\n\t\treturn prefix(System.currentTimeMillis()) | sub;\n\t}\n\n\tprivate long prefix(long time) {\n\t\tlong num = shortMills(time);\n\t\tnum &= 0xFFFFFFFFFFL;\n\t\treturn num << 24;\n\t}\n\n\t@Override\n\tpublic long getTimeMillis(long seq) {\n\t\tlong num = seq & 0xFFFFFFFFFF000000L;\n\t\tnum >>>= 24;\n\t\treturn fullTime(num);\n\t}\n\n\t@Override\n\tpublic long low(SumkDate date) {\n\t\treturn prefix(date.getTimeInMils());\n\t}\n\n\t@Override\n\tpublic long high(SumkDate date) {\n\t\treturn prefix(date.getTimeInMils()) | 0xFFFFFF;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/sequence/SnowflakeCounter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.sequence;\n\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class SnowflakeCounter implements SeqCounter {\n\n\tprivate final int snow;\n\tprivate final AtomicInteger seq;\n\n\t@Override\n\tpublic int incr(String name) throws Exception {\n\t\tint random = seq.incrementAndGet() & 0xFFFF;\n\t\trandom <<= 8;\n\t\treturn snow | random;\n\t}\n\n\tpublic SnowflakeCounter(int snow) {\n\t\tthis.snow = snow & 0xFF;\n\t\tseq = new AtomicInteger(ThreadLocalRandom.current().nextInt());\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/S.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util;\n\nimport org.yx.base.thread.SumkExecutorService;\nimport org.yx.common.json.GsonHelper;\nimport org.yx.common.lock.Locker;\nimport org.yx.common.util.kit.BeanConverter;\nimport org.yx.common.util.secury.AESEncryptor;\nimport org.yx.common.util.secury.Base64;\nimport org.yx.common.util.secury.Base64Impl;\nimport org.yx.common.util.secury.CommonDigest;\nimport org.yx.common.util.secury.Encryptor;\nimport org.yx.common.util.secury.Hasher;\nimport org.yx.util.SumkThreadPool;\n\nimport com.google.gson.Gson;\n\npublic final class S {\n\n\tprivate static Gson json = GsonHelper.gson(\"sumk\");\n\tprivate static SumkExecutorService executor = SumkThreadPool.executor();\n\tprivate static Base64 base64 = Base64Impl.inst;\n\tprivate static Encryptor cipher = new AESEncryptor();\n\tprivate static Hasher hash = new CommonDigest(\"SHA-256\");\n\tprivate static Locker lock = Locker.inst;\n\tprivate static BeanConverter bean = new BeanConverter();\n\n\t/**\n\t * @return json工具\n\t */\n\tpublic static Gson json() {\n\t\treturn json;\n\t}\n\n\t/**\n\t * @return 系统共用的线程池\n\t */\n\tpublic static SumkExecutorService executor() {\n\t\treturn executor;\n\t}\n\n\tpublic static Base64 base64() {\n\t\treturn base64;\n\t}\n\n\t/**\n\t * @return 加密器，默认是AES对称加密\n\t */\n\tpublic static Encryptor cipher() {\n\t\treturn cipher;\n\t}\n\n\t/**\n\t * @return hash工具，就是大家常说的md5\n\t */\n\tpublic static Hasher hash() {\n\t\treturn hash;\n\t}\n\n\t/**\n\t * @return 分布式锁\n\t */\n\tpublic static Locker lock() {\n\t\treturn lock;\n\t}\n\n\t/**\n\t * @return bean和map的转换，以及属性复制。只支持第一级field\n\t */\n\tpublic static BeanConverter bean() {\n\t\treturn bean;\n\t}\n\n\tstatic void setJson(Gson json) {\n\t\tS.json = json;\n\t}\n\n\tstatic void setExecutor(SumkExecutorService executor) {\n\t\tS.executor = executor;\n\t}\n\n\tstatic void setBase64(Base64 base64) {\n\t\tS.base64 = base64;\n\t}\n\n\tstatic void setCipher(Encryptor cipher) {\n\t\tS.cipher = cipher;\n\t}\n\n\tstatic void setHash(Hasher hash) {\n\t\tS.hash = hash;\n\t}\n\n\tstatic void setLock(Locker lock) {\n\t\tS.lock = lock;\n\t}\n\n\tstatic void setBean(BeanConverter bean) {\n\t\tS.bean = bean;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/SBuilder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.yx.base.thread.SumkExecutorService;\nimport org.yx.common.lock.Locker;\nimport org.yx.common.util.kit.BeanConverter;\nimport org.yx.common.util.secury.Base64;\nimport org.yx.common.util.secury.Encryptor;\nimport org.yx.common.util.secury.Hasher;\n\nimport com.google.gson.Gson;\n\npublic final class SBuilder {\n\tpublic static class MapBuilder<K, V> {\n\t\tprivate final Map<K, V> map;\n\n\t\tpublic MapBuilder(Map<K, V> map) {\n\t\t\tthis.map = map;\n\t\t}\n\n\t\tpublic MapBuilder<K, V> put(K key, V value) {\n\t\t\tmap.put(key, value);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic MapBuilder<K, V> remove(K key) {\n\t\t\tmap.remove(key);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Map<K, V> toMap() {\n\t\t\treturn this.map;\n\t\t}\n\t}\n\n\tpublic static <T> MapBuilder<String, T> map(Class<T> valueType) {\n\t\treturn new MapBuilder<>(new HashMap<String, T>());\n\t}\n\n\tpublic static <K, V> MapBuilder<K, V> map(Map<K, V> map) {\n\t\treturn new MapBuilder<>(map);\n\t}\n\n\tpublic static MapBuilder<String, Object> map() {\n\t\treturn new MapBuilder<>(new HashMap<String, Object>());\n\t}\n\n\tpublic static MapBuilder<String, Object> map(String key, Object value) {\n\t\treturn map().put(key, value);\n\t}\n\n\tpublic static void setJson(Gson json) {\n\t\tS.setJson(Objects.requireNonNull(json));\n\t}\n\n\tpublic static void setExecutor(SumkExecutorService executor) {\n\t\tS.setExecutor(Objects.requireNonNull(executor));\n\t}\n\n\tpublic static void setBase64(Base64 base64) {\n\t\tS.setBase64(Objects.requireNonNull(base64));\n\t}\n\n\tpublic static void setCipher(Encryptor cipher) {\n\t\tS.setCipher(Objects.requireNonNull(cipher));\n\t}\n\n\tpublic static void setHash(Hasher hash) {\n\t\tS.setHash(Objects.requireNonNull(hash));\n\t}\n\n\tpublic static void setLock(Locker lock) {\n\t\tS.setLock(Objects.requireNonNull(lock));\n\t}\n\n\tpublic static void setBean(BeanConverter bean) {\n\t\tS.setBean(Objects.requireNonNull(bean));\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/SeqUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util;\n\nimport org.yx.common.sequence.SeqHolder;\nimport org.yx.util.SumkDate;\n\npublic final class SeqUtil {\n\n\tpublic static long next() {\n\t\treturn SeqHolder.inst().next();\n\t}\n\n\tpublic static long next(String name) {\n\t\treturn SeqHolder.inst().next(name);\n\t}\n\n\tpublic static String nextString() {\n\t\treturn Long.toString(next(), Character.MAX_RADIX);\n\t}\n\n\tpublic static String nextString(String name) {\n\t\treturn Long.toString(next(name), Character.MAX_RADIX);\n\t}\n\n\tpublic static long getTimeMillis(long seq) {\n\t\treturn SeqHolder.inst().getTimeMillis(seq);\n\t}\n\n\tpublic static SumkDate toSumkDate(long seq) {\n\t\treturn SumkDate.of(getTimeMillis(seq));\n\t}\n\n\tpublic static long from(SumkDate date) {\n\t\treturn SeqHolder.inst().low(date);\n\t}\n\n\tpublic static long to(SumkDate date) {\n\t\treturn SeqHolder.inst().high(date);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/helper/ArrayHelper.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.helper;\n\nimport java.lang.reflect.Array;\nimport java.util.Objects;\n\npublic final class ArrayHelper {\n\n\tpublic static <T> T[] add(T[] old, T obj, Class<T> clz) {\n\t\tif (obj == null) {\n\t\t\treturn old;\n\t\t}\n\t\tif (old == null || old.length == 0) {\n\t\t\tT[] ret = createArray(clz, 1);\n\t\t\tret[0] = obj;\n\t\t\treturn ret;\n\t\t}\n\t\tfor (T f : old) {\n\t\t\tif (f.equals(obj)) {\n\t\t\t\treturn old;\n\t\t\t}\n\t\t}\n\t\tT[] ret = createArray(clz, old.length + 1);\n\t\tSystem.arraycopy(old, 0, ret, 0, old.length);\n\t\tret[old.length] = obj;\n\t\treturn ret;\n\t}\n\n\tpublic static <T> T[] remove(T[] old, T obj, Class<T> clz) {\n\t\tif (old == null || old.length == 0 || obj == null) {\n\t\t\treturn old;\n\t\t}\n\t\tfor (int i = 0; i < old.length; i++) {\n\t\t\tT f = old[i];\n\t\t\tif (Objects.equals(f, obj)) {\n\t\t\t\tT[] ret = createArray(clz, old.length - 1);\n\t\t\t\tSystem.arraycopy(old, 0, ret, 0, i);\n\t\t\t\tSystem.arraycopy(old, i + 1, ret, i, ret.length - i);\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t}\n\t\treturn old;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T> T[] createArray(Class<T> clz, int length) {\n\t\treturn (T[]) Array.newInstance(clz, length);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/kit/Asserts.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.kit;\n\nimport org.yx.exception.SumkException;\n\npublic final class Asserts {\n\n\tpublic static void requireTrue(boolean b, String msg) {\n\t\tif (b) {\n\t\t\treturn;\n\t\t}\n\t\tthrow new SumkException(5674354, msg);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/kit/BeanConverter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.kit;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\n\nimport org.yx.exception.SumkException;\nimport org.yx.util.Loader;\n\n/**\n * 支持子类，但不支持属性的嵌套解析\n */\n\npublic class BeanConverter {\n\tprivate static final String JAVA_PRE = \"java\";\n\n\tprivate Map<Class<?>, Field[]> cache = Collections\n\t\t\t.synchronizedMap(new LinkedHashMap<Class<?>, Field[]>(32, 0.75f, true) {\n\t\t\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t\t\t@Override\n\t\t\t\tprotected boolean removeEldestEntry(Entry<Class<?>, Field[]> eldest) {\n\t\t\t\t\treturn this.size() > 800;\n\t\t\t\t}\n\n\t\t\t});\n\n\tprivate Field[] parseFields(Class<?> clz) {\n\t\tList<Field> list = new ArrayList<>();\n\t\tClass<?> tempClz = clz;\n\t\twhile (tempClz != null && tempClz != Object.class) {\n\n\t\t\tField[] fs = cache.get(tempClz);\n\t\t\tif (fs != null) {\n\t\t\t\tCollections.addAll(list, fs);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfs = tempClz.getDeclaredFields();\n\t\t\tfor (Field f : fs) {\n\t\t\t\tif ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT | Modifier.FINAL)) != 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!f.isAccessible()) {\n\t\t\t\t\tf.setAccessible(true);\n\t\t\t\t}\n\t\t\t\tlist.add(f);\n\t\t\t}\n\t\t\ttempClz = tempClz.getSuperclass();\n\t\t}\n\t\treturn list.toArray(new Field[list.size()]);\n\t}\n\n\tprivate Field[] getFieldsAndCache(Class<?> clz) {\n\t\tField[] fields = getFields(clz);\n\t\tField[] f2 = cache.putIfAbsent(clz, fields);\n\t\treturn f2 != null ? f2 : fields;\n\t}\n\n\tpublic Field[] getFields(Class<?> clz) {\n\t\tField[] fields = cache.get(clz);\n\t\tif (fields != null) {\n\t\t\treturn fields;\n\t\t}\n\t\tfields = this.parseFields(clz);\n\t\tString clzName = clz.getName();\n\t\tif (clzName.startsWith(JAVA_PRE) || clzName.contains(\"$\")) {\n\t\t\treturn fields;\n\t\t}\n\t\treturn fields;\n\t}\n\n\t/**\n\t * 根据field转为map。不会对属性内部的字段再做解析\n\t * \n\t * @param bean     用于转化的pojo对象。\n\t * @param keepNull 如果为true，那么它就会保留null字段\n\t * @return 返回map对象，不为null。\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic Map<String, Object> beanToMap(Object bean, boolean keepNull) {\n\t\tif (bean == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tif (bean instanceof Map) {\n\t\t\treturn (Map<String, Object>) bean;\n\t\t}\n\t\ttry {\n\t\t\tField[] fields = getFieldsAndCache(bean.getClass());\n\t\t\tMap<String, Object> map = new HashMap<>();\n\t\t\tfor (Field f : fields) {\n\t\t\t\tObject v = f.get(bean);\n\t\t\t\tif (v == null && !keepNull) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmap.putIfAbsent(f.getName(), v);\n\t\t\t}\n\t\t\treturn map;\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(35432540, \"beanToMap to map failed, because of \" + e.getMessage(), e);\n\t\t}\n\t}\n\n\t/**\n\t * 根据map的内容填充bean的属性。只转义第一层的key。<BR>\n\t * 对大小写和下划线不敏感\n\t * \n\t * @param <T>  参数类型\n\t * @param map  原始map\n\t * @param bean 目标对象，它的属性会被填充进来\n\t * @return 返回目标对象\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> T fillBeanIgnoreCaseAndUnderLine(Map<String, Object> map, T bean) {\n\t\tif (map == null || bean == null || map.isEmpty()) {\n\t\t\treturn bean;\n\t\t}\n\t\tif (bean instanceof Map) {\n\t\t\t((Map<String, Object>) bean).putAll(map);\n\t\t\treturn bean;\n\t\t}\n\t\tMap<String, Object> tmp = new HashMap<>();\n\t\tfor (Entry<String, Object> entry : map.entrySet()) {\n\t\t\tString k = entry.getKey();\n\t\t\tObject v = entry.getValue();\n\t\t\tif (k == null || v == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttmp.put(k.toLowerCase().replace(\"_\", \"\"), v);\n\t\t}\n\t\tmap = tmp;\n\t\tField[] fields = getFieldsAndCache(bean.getClass());\n\t\ttry {\n\t\t\tfor (Field f : fields) {\n\t\t\t\tString fName = f.getName().toLowerCase().replace(\"_\", \"\");\n\t\t\t\tif (map.containsKey(fName)) {\n\t\t\t\t\tf.set(bean, TypeConverter.convert(map.get(fName), f.getType()));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(35432541, \"fillBean failed, because of \" + e.getMessage(), e);\n\t\t}\n\t\treturn bean;\n\t}\n\n\t/**\n\t * 根据map的内容填充bean的属性。只转义第一层的key。\n\t * \n\t * @param <T>  参数类型\n\t * @param map  原始map\n\t * @param bean 目标对象，它的属性会被填充进来\n\t * @return 返回目标对象\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> T fillBean(Map<String, Object> map, T bean) {\n\t\tif (map == null || bean == null || map.isEmpty()) {\n\t\t\treturn bean;\n\t\t}\n\t\tif (bean instanceof Map) {\n\t\t\t((Map<String, Object>) bean).putAll(map);\n\t\t\treturn bean;\n\t\t}\n\t\tField[] fields = getFieldsAndCache(bean.getClass());\n\t\ttry {\n\t\t\tfor (Field f : fields) {\n\t\t\t\tif (map.containsKey(f.getName())) {\n\t\t\t\t\tf.set(bean, TypeConverter.convert(map.get(f.getName()), f.getType()));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(35432541, \"fillBean failed, because of \" + e.getMessage(), e);\n\t\t}\n\t\treturn bean;\n\t}\n\n\tpublic <T> T copyFields(Object src, T dest) {\n\t\tif (src == null || dest == null) {\n\t\t\treturn dest;\n\t\t}\n\t\tClass<?> srcClz = src.getClass();\n\t\tif (!srcClz.isInstance(dest)) {\n\t\t\tMap<String, Object> map = this.beanToMap(src, false);\n\t\t\treturn this.fillBean(map, dest);\n\t\t}\n\t\tField[] fields = getFieldsAndCache(srcClz);\n\t\ttry {\n\t\t\tfor (Field f : fields) {\n\t\t\t\tf.set(dest, f.get(src));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(35432542, \"copyFields failed, because of \" + e.getMessage(), e);\n\t\t}\n\t\treturn dest;\n\t}\n\n\tpublic Object clone(Object src) {\n\t\tif (src == null) {\n\t\t\treturn null;\n\t\t}\n\t\tObject dest;\n\t\ttry {\n\t\t\tdest = Loader.newInstance(src.getClass());\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(35432545, \"clone failed, because of \" + e.getMessage(), e);\n\t\t}\n\t\treturn this.copyFields(src, dest);\n\t}\n\n\tpublic Map<Class<?>, Field[]> getCache() {\n\t\treturn cache;\n\t}\n\n\tpublic void setCache(Map<Class<?>, Field[]> threadSaftMap) {\n\t\tthis.cache = Objects.requireNonNull(threadSaftMap);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/kit/PriorityKits.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.kit;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport org.yx.annotation.Priority;\nimport org.yx.base.Ordered;\n\npublic class PriorityKits {\n\n\tprivate static final Comparator<Class<?>> priorityComparator = (a, b) -> {\n\t\tint pa = getPriority(a);\n\t\tint pb = getPriority(b);\n\t\tif (pa == pb) {\n\t\t\treturn a.getName().compareTo(b.getName());\n\t\t}\n\t\treturn Integer.compare(pa, pb);\n\t};\n\n\tpublic static int getPriority(Class<?> clz) {\n\t\tPriority p = clz.getAnnotation(Priority.class);\n\t\tif (p == null) {\n\t\t\treturn Ordered.DEFAULT_ORDER;\n\t\t}\n\t\treturn p.value();\n\t}\n\n\tpublic static List<Class<?>> sort(Collection<Class<?>> source) {\n\t\tList<Class<?>> list = new ArrayList<>(source);\n\t\tlist.sort(priorityComparator);\n\t\treturn list;\n\t}\n\n\t/**\n\t * \n\t * @param sortedClasses 已排序好的列表\n\t * @return 会有3个item，第一个是Priority小于默认值的，第二个是等于默认值或者没有@Priority注解，第三个Priority大于默认值的\n\t */\n\tpublic static List<List<Class<?>>> split(List<Class<?>> sortedClasses) {\n\t\tList<Class<?>> low = new ArrayList<>();\n\t\tList<Class<?>> high = new ArrayList<>();\n\t\tList<Class<?>> middle = new ArrayList<>();\n\t\tfinal int size = sortedClasses.size();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tClass<?> clz = sortedClasses.get(i);\n\t\t\tint p = getPriority(clz);\n\t\t\tif (p == Ordered.DEFAULT_ORDER) {\n\t\t\t\tmiddle.add(clz);\n\t\t\t} else if (p < Ordered.DEFAULT_ORDER) {\n\t\t\t\tlow.add(clz);\n\t\t\t} else {\n\t\t\t\thigh.add(clz);\n\t\t\t}\n\t\t}\n\t\treturn Arrays.asList(low, middle, high);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/kit/TypeConverter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.kit;\n\nimport java.math.BigDecimal;\nimport java.sql.Blob;\nimport java.sql.Clob;\nimport java.util.Objects;\nimport java.util.function.BiFunction;\n\nimport org.yx.base.date.TimeUtil;\nimport org.yx.exception.SumkException;\nimport org.yx.util.IOUtil;\n\npublic class TypeConverter {\n\n\tprivate static BiFunction<Object, Class<?>, Object> customConverter = (value, type) -> value;\n\n\tpublic static BiFunction<Object, Class<?>, Object> getCustomConverter() {\n\t\treturn customConverter;\n\t}\n\n\tpublic static void setCustomConverter(BiFunction<Object, Class<?>, Object> customConverter) {\n\t\tTypeConverter.customConverter = Objects.requireNonNull(customConverter);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T> T convert(Object value, Class<T> type) throws Exception {\n\t\tif (value == null || type == value.getClass()) {\n\t\t\treturn (T) value;\n\t\t}\n\t\tif (TimeUtil.isGenericDate(type)) {\n\t\t\treturn TimeUtil.toType(value, type, false);\n\t\t}\n\t\tif (type.isInstance(value)) {\n\t\t\treturn type.cast(value);\n\t\t}\n\n\t\tif (Number.class.isAssignableFrom(type) && value instanceof Number) {\n\t\t\tT t = (T) toType((Number) value, type, false);\n\t\t\tif (t != null) {\n\t\t\t\treturn t;\n\t\t\t}\n\t\t}\n\t\tif (type == byte[].class && value instanceof Blob) {\n\t\t\tBlob v = (Blob) value;\n\t\t\treturn (T) IOUtil.readAllBytes(v.getBinaryStream(), true);\n\t\t}\n\t\tif (type == String.class && value instanceof Clob) {\n\t\t\tClob v = (Clob) value;\n\t\t\treturn (T) IOUtil.readAll(v.getCharacterStream(), true);\n\t\t}\n\t\treturn (T) customConverter.apply(value, type);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T extends Number> T toType(Number v, Class<?> type, boolean failIfNotSupport) {\n\t\tif (v.getClass() == type) {\n\t\t\treturn (T) v;\n\t\t}\n\t\tif (Integer.class == type || int.class == type) {\n\t\t\treturn (T) Integer.valueOf(v.intValue());\n\t\t}\n\t\tif (Long.class == type || long.class == type) {\n\t\t\treturn (T) Long.valueOf(v.longValue());\n\t\t}\n\t\tif (Double.class == type || double.class == type) {\n\t\t\treturn (T) Double.valueOf(v.doubleValue());\n\t\t}\n\n\t\tif (Byte.class == type || byte.class == type) {\n\t\t\treturn (T) Byte.valueOf(v.byteValue());\n\t\t}\n\t\tif (Short.class == type || short.class == type) {\n\t\t\treturn (T) Short.valueOf(v.shortValue());\n\t\t}\n\t\tif (Float.class == type || float.class == type) {\n\t\t\treturn (T) Float.valueOf(v.floatValue());\n\t\t}\n\n\t\tif (BigDecimal.class == type) {\n\t\t\tif (v instanceof Integer || v instanceof Long || v instanceof Short || v instanceof Byte) {\n\t\t\t\treturn (T) new BigDecimal(v.longValue());\n\t\t\t}\n\t\t\treturn (T) new BigDecimal(v.doubleValue());\n\t\t}\n\t\tif (failIfNotSupport || !Number.class.isAssignableFrom(type)) {\n\t\t\tthrow new SumkException(927816546, type.getClass().getName() + \"is not valid Number type\");\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/secury/AESEncryptor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.secury;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport org.yx.log.Logs;\n\npublic class AESEncryptor implements Encryptor {\n\n\tprivate static final String AES = \"AES\";\n\n\tprivate String c = \"AES/CBC/PKCS5Padding\";\n\n\tpublic AESEncryptor() {\n\t}\n\n\tpublic AESEncryptor(String cipher) {\n\t\tif (cipher != null && cipher.length() > 1) {\n\t\t\tthis.c = cipher;\n\t\t\tLogs.system().info(\"aes cipher:{}\", this.c);\n\t\t}\n\t}\n\n\tpublic String getCipherTransformation() {\n\t\treturn c;\n\t}\n\n\tprotected Cipher getCipher(int mode, byte[] key) throws Exception {\n\t\tCipher cipher = Cipher.getInstance(c);\n\t\tif (c.contains(\"ECB\")) {\n\t\t\tcipher.init(mode, new SecretKeySpec(key, AES));\n\t\t} else {\n\t\t\tcipher.init(mode, new SecretKeySpec(key, AES), new IvParameterSpec(key));\n\t\t}\n\t\treturn cipher;\n\t}\n\n\t@Override\n\tpublic byte[] encrypt(byte[] contentBytes, byte[] key) throws Exception {\n\t\tif (contentBytes == null || contentBytes.length == 0) {\n\t\t\treturn contentBytes;\n\t\t}\n\t\treturn getCipher(Cipher.ENCRYPT_MODE, key).doFinal(contentBytes);\n\t}\n\n\t@Override\n\tpublic byte[] decrypt(byte[] contentBytes, byte[] key) throws Exception {\n\t\tif (contentBytes == null || contentBytes.length == 0) {\n\t\t\treturn contentBytes;\n\t\t}\n\t\treturn getCipher(Cipher.DECRYPT_MODE, key).doFinal(contentBytes);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/secury/Base64.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.secury;\n\npublic interface Base64 {\n\n\t/**\n\t * 解码，是否含有\\r\\n都能解码\n\t * \n\t * @param src 密文\n\t * @return 明文\n\t */\n\tbyte[] decode(byte[] src);\n\n\t/**\n\t * 解码，是否含有\\r\\n都能解码\n\t * \n\t * @param src 密文\n\t * @return 明文\n\t */\n\tbyte[] decode(String src);\n\n\t/**\n\t * 使用标准方式进行编码,不含有换行符\n\t * \n\t * @param src 原文\n\t * @return 编码后的\n\t */\n\tbyte[] encode(byte[] src);\n\n\tString encodeToString(byte[] src);\n\n}"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/secury/Base64Impl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.secury;\n\nimport java.util.Base64.Decoder;\nimport java.util.Base64.Encoder;\n\nimport org.yx.conf.AppInfo;\n\npublic final class Base64Impl implements Base64 {\n\n\tpublic static final Base64 inst = new Base64Impl(java.util.Base64.getEncoder(), java.util.Base64.getMimeDecoder());\n\n\tprivate final Decoder decoder;\n\n\tprivate final Encoder encoder;\n\n\tpublic Base64Impl(Encoder encoder, Decoder decoder) {\n\t\tthis.encoder = encoder;\n\t\tthis.decoder = decoder;\n\t}\n\n\t@Override\n\tpublic byte[] decode(byte[] src) {\n\t\treturn decoder.decode(src);\n\t}\n\n\t@Override\n\tpublic byte[] decode(String src) {\n\t\treturn decoder.decode(src.getBytes(AppInfo.UTF8));\n\t}\n\n\t@Override\n\tpublic byte[] encode(byte[] src) {\n\t\treturn encoder.encode(src);\n\t}\n\n\t@Override\n\tpublic String encodeToString(byte[] src) {\n\t\treturn new String(encoder.encode(src), AppInfo.UTF8);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/secury/CommonDigest.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.secury;\n\nimport java.nio.charset.Charset;\nimport java.security.MessageDigest;\n\npublic class CommonDigest implements Hasher {\n\tprotected final String algorithm;\n\n\tpublic CommonDigest(String algorithm) {\n\t\tthis.algorithm = algorithm;\n\t}\n\n\tpublic String digest(String data, Charset charset) throws Exception {\n\t\treturn digestByteToString(data.getBytes(charset));\n\t}\n\n\tpublic byte[] digest(byte[] data) throws Exception {\n\t\tMessageDigest md = MessageDigest.getInstance(algorithm);\n\t\tmd.update(data);\n\t\treturn md.digest();\n\t}\n\n\tpublic String parseByte2HexStr(byte buf[]) {\n\t\tStringBuilder sb = new StringBuilder(32);\n\t\tfor (int i = 0; i < buf.length; i++) {\n\t\t\tString hex = Integer.toHexString(buf[i] & 0xFF);\n\t\t\tif (hex.length() == 1) {\n\t\t\t\thex = '0' + hex;\n\t\t\t}\n\t\t\tsb.append(hex);\n\t\t}\n\t\treturn sb.toString().toLowerCase();\n\t}\n\n\t@Override\n\tpublic String digestByteToString(byte[] data) throws Exception {\n\t\treturn parseByte2HexStr(digest(data));\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/secury/Encryptor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.secury;\n\npublic interface Encryptor {\n\n\tpublic byte[] encrypt(byte[] contentBytes, byte[] key) throws Exception;\n\n\tpublic byte[] decrypt(byte[] contentBytes, byte[] key) throws Exception;\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/util/secury/Hasher.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.util.secury;\n\nimport java.nio.charset.Charset;\n\npublic interface Hasher {\n\n\tString digest(String data, Charset charset) throws Exception;\n\n\tbyte[] digest(byte[] data) throws Exception;\n\n\tString digestByteToString(byte[] data) throws Exception;\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/AbstractParamInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\nimport java.util.Objects;\n\nimport org.yx.annotation.spec.ParamSpec;\nimport org.yx.conf.AppInfo;\n\npublic abstract class AbstractParamInfo implements ParameterInfo {\n\tprivate final ParamSpec param;\n\tprivate final boolean required;\n\tprivate final boolean complex;\n\tprivate final boolean maybeCheck;\n\n\tpublic AbstractParamInfo(ParamSpec param, Class<?> type) {\n\t\tthis.param = Objects.requireNonNull(param);\n\t\tthis.required = param.required() && AppInfo.getBoolean(\"sumk.param.required.enable\", true);\n\t\tthis.complex = Validators.supportComplex(Objects.requireNonNull(type)) && param.complex();\n\n\t\tthis.maybeCheck = this.required || this.complex || param.max() >= 0 || param.min() >= 0\n\t\t\t\t|| param.custom() != null;\n\t}\n\n\t@Override\n\tpublic Object custom() {\n\t\treturn this.param.custom();\n\t}\n\n\t@Override\n\tpublic String example() {\n\t\treturn this.param.example();\n\t}\n\n\t@Override\n\tpublic String comment() {\n\t\treturn this.param.comment();\n\t}\n\n\tpublic ParamSpec getParam() {\n\t\treturn param;\n\t}\n\n\tpublic boolean isRequired() {\n\t\treturn required;\n\t}\n\n\tpublic int getMax() {\n\t\treturn param.max();\n\t}\n\n\tpublic int getMin() {\n\t\treturn param.min();\n\t}\n\n\t@Override\n\tpublic String getCnName() {\n\t\treturn this.param.value();\n\t}\n\n\tpublic boolean isComplex() {\n\t\treturn complex;\n\t}\n\n\tpublic boolean maybeCheck() {\n\t\treturn maybeCheck;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/ComplexParamValidator.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\nimport java.lang.reflect.Array;\nimport java.util.List;\n\nimport org.yx.annotation.Bean;\nimport org.yx.exception.SimpleSumkException;\nimport org.yx.log.Logs;\n\n@Bean\npublic class ComplexParamValidator implements Validator {\n\n\t@Override\n\tpublic void valid(final ParameterInfo info, Object arg) throws InvalidParamException {\n\t\tif (info == null || arg == null) {\n\t\t\treturn;\n\t\t}\n\t\tClass<?> clz = arg.getClass();\n\t\tif (clz.isArray()) {\n\t\t\tint length = Array.getLength(arg);\n\t\t\tif (length == 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (int i = 0; i < length; i++) {\n\t\t\t\tValidators.check(info, Array.get(arg, i));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!info.isComplex()) {\n\t\t\treturn;\n\t\t}\n\t\tthis.checkFields(FieldParameterHolder.get(clz), arg);\n\t}\n\n\tprotected void checkFields(List<FieldParameterInfo> infos, Object obj) throws InvalidParamException {\n\t\tif (infos == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tfor (FieldParameterInfo info : infos) {\n\t\t\t\tValidators.check(info, info.field.get(obj));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tif (e instanceof InvalidParamException) {\n\t\t\t\tthrow (InvalidParamException) e;\n\t\t\t}\n\t\t\tLogs.system().warn(\"参数校验发生异常，\" + e.getLocalizedMessage(), e);\n\t\t\tthrow new SimpleSumkException(5346614, \"校验时发生异常，异常信息为\" + e.getLocalizedMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn 10000;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/FieldParameterHolder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.yx.annotation.spec.ParamSpec;\nimport org.yx.annotation.spec.Specs;\nimport org.yx.util.CollectionUtil;\n\npublic final class FieldParameterHolder {\n\tprivate static final ConcurrentMap<Class<?>, List<FieldParameterInfo>> map = new ConcurrentHashMap<>();\n\n\tpublic static void put(Class<?> clz, List<FieldParameterInfo> infos) {\n\t\tif (clz == null || infos == null || infos.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tmap.put(clz, infos);\n\t}\n\n\tpublic static Set<Class<?>> keys() {\n\t\treturn new HashSet<>(map.keySet());\n\t}\n\n\tpublic static List<FieldParameterInfo> get(Class<?> clz) {\n\t\treturn map.get(clz);\n\t}\n\n\tpublic static Map<Field, FieldParameterInfo> getFieldParameterMap(Class<?> clz) {\n\t\tList<FieldParameterInfo> infos = get(clz);\n\t\tif (infos == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMap<Field, FieldParameterInfo> infoMap = new HashMap<>();\n\t\tfor (FieldParameterInfo info : infos) {\n\t\t\tinfoMap.put(info.getField(), info);\n\t\t}\n\t\treturn infoMap;\n\t}\n\n\tpublic static void registerFieldInfo(final Class<?> clazz) {\n\t\tif (clazz.isArray()) {\n\t\t\tregisterFieldInfo(clazz.getComponentType());\n\t\t\treturn;\n\t\t}\n\t\tif (!Validators.supportComplex(clazz)) {\n\t\t\treturn;\n\t\t}\n\t\tif (get(clazz) != null) {\n\t\t\treturn;\n\t\t}\n\t\tList<FieldParameterInfo> list = new ArrayList<>();\n\t\tClass<?> tempClz = clazz;\n\t\twhile (tempClz != null && !tempClz.getName().startsWith(\"java.\")) {\n\n\t\t\tField[] fs = tempClz.getDeclaredFields();\n\t\t\tfor (Field f : fs) {\n\t\t\t\tif (Modifier.isStatic(f.getModifiers())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tParamSpec p = Specs.extractParamField(f);\n\t\t\t\tif (p == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tFieldParameterInfo info = new FieldParameterInfo(p, f);\n\n\t\t\t\tif (!info.maybeCheck()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlist.add(info);\n\t\t\t\tif (info.isComplex()) {\n\t\t\t\t\tregisterFieldInfo(info.getParamType());\n\t\t\t\t}\n\t\t\t}\n\t\t\ttempClz = tempClz.getSuperclass();\n\t\t}\n\n\t\tif (list.size() > 0) {\n\t\t\tput(clazz, CollectionUtil.unmodifyList(list.toArray(new FieldParameterInfo[list.size()])));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/FieldParameterInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\nimport java.lang.reflect.Field;\nimport java.util.Objects;\n\nimport org.yx.annotation.spec.ParamSpec;\n\npublic class FieldParameterInfo extends AbstractParamInfo {\n\n\tprotected final Field field;\n\n\tpublic FieldParameterInfo(ParamSpec param, Field field) {\n\t\tsuper(param, Objects.requireNonNull(field).getType());\n\t\tthis.field = field;\n\t\tif (!this.field.isAccessible()) {\n\t\t\tthis.field.setAccessible(true);\n\t\t}\n\t}\n\n\tpublic String getParamName() {\n\t\treturn field.getName();\n\t}\n\n\tpublic Class<?> getParamType() {\n\t\treturn field.getType();\n\t}\n\n\tpublic Field getField() {\n\t\treturn field;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/InvalidParamException.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.util.StringUtil;\n\n/**\n * 这个异常表示是参数验证失败\n * \n * @author 游夏\n *\n */\npublic class InvalidParamException extends Exception {\n\n\tprivate static final long serialVersionUID = 125465546L;\n\n\tpublic static final String PARAM_SLOT = \"$@$\";\n\n\tprivate ParameterInfo info;\n\n\tpublic InvalidParamException(String message, ParameterInfo info) {\n\t\tsuper(message);\n\t\tthis.info = info;\n\t}\n\n\t@Override\n\tpublic String getMessage() {\n\t\tString ret = super.getMessage();\n\t\tif (ret.indexOf(PARAM_SLOT) < 0) {\n\t\t\treturn ret;\n\t\t}\n\t\tif (info == null) {\n\t\t\treturn ret.replace(PARAM_SLOT, genericParamName());\n\t\t}\n\t\tif (AppInfo.getBoolean(\"sumk.valid.name.cn\", true) && StringUtil.isNotEmpty(info.getCnName())) {\n\t\t\treturn ret.replace(PARAM_SLOT, info.getCnName());\n\t\t}\n\t\treturn ret.replace(PARAM_SLOT, info.getParamName());\n\t}\n\n\tprivate String genericParamName() {\n\t\treturn AppInfo.get(\"sumk.valid.name.generic\", \"参数\");\n\t}\n\n\tpublic ParameterInfo getInfo() {\n\t\treturn info;\n\t}\n\n\t@Override\n\tpublic Throwable fillInStackTrace() {\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/ManuParameterInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\npublic class ManuParameterInfo implements ParameterInfo {\n\n\tprivate String paramName;\n\tprivate String cnName;\n\tprivate boolean required;\n\tprivate Integer max;\n\tprivate Integer min;\n\tprivate Class<?> paramType;\n\tprivate boolean complex;\n\tprivate String custom;\n\tprivate String example;\n\tprivate String comment;\n\n\tpublic boolean isComplex() {\n\t\treturn complex;\n\t}\n\n\tpublic void setComplex(boolean complex) {\n\t\tthis.complex = complex;\n\t}\n\n\tpublic String getParamName() {\n\t\treturn paramName;\n\t}\n\n\tpublic void setParamName(String paramName) {\n\t\tthis.paramName = paramName;\n\t}\n\n\tpublic String getCnName() {\n\t\treturn cnName;\n\t}\n\n\tpublic void setCnName(String cnName) {\n\t\tthis.cnName = cnName;\n\t}\n\n\tpublic boolean isRequired() {\n\t\treturn required;\n\t}\n\n\tpublic void setRequired(boolean required) {\n\t\tthis.required = required;\n\t}\n\n\tpublic int getMax() {\n\t\treturn max == null ? -1 : max;\n\t}\n\n\tpublic void setMax(Integer max) {\n\t\tthis.max = max;\n\t}\n\n\tpublic int getMin() {\n\t\treturn min == null ? -1 : min;\n\t}\n\n\tpublic void setMin(Integer min) {\n\t\tthis.min = min;\n\t}\n\n\tpublic Class<?> getParamType() {\n\t\treturn paramType;\n\t}\n\n\tpublic void setParamType(Class<?> paramType) {\n\t\tthis.paramType = paramType;\n\t}\n\n\tpublic String custom() {\n\t\treturn custom;\n\t}\n\n\tpublic void setCustom(String custom) {\n\t\tthis.custom = custom;\n\t}\n\n\tpublic String example() {\n\t\treturn example;\n\t}\n\n\tpublic void setExample(String example) {\n\t\tthis.example = example;\n\t}\n\n\tpublic String comment() {\n\t\treturn comment;\n\t}\n\n\tpublic void setComment(String comment) {\n\t\tthis.comment = comment;\n\t}\n\n\t@Override\n\tpublic boolean maybeCheck() {\n\t\treturn this.required || this.complex || max >= 0 || min >= 0 || custom != null;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/ParamInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\nimport java.util.Objects;\n\nimport org.yx.annotation.spec.ParamSpec;\n\npublic class ParamInfo extends AbstractParamInfo {\n\n\tprivate final String paramName;\n\n\tprivate final Class<?> paramType;\n\n\tpublic ParamInfo(ParamSpec param, String paramName, Class<?> type) {\n\t\tsuper(param, type);\n\t\tthis.paramName = Objects.requireNonNull(paramName);\n\t\tthis.paramType = type;\n\t}\n\n\tpublic String getParamName() {\n\t\treturn paramName;\n\t}\n\n\tpublic Class<?> getParamType() {\n\t\treturn paramType;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/ParameterInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\npublic interface ParameterInfo {\n\n\tString getParamName();\n\n\tString getCnName();\n\n\tboolean isRequired();\n\n\tint getMax();\n\n\tint getMin();\n\n\tClass<?> getParamType();\n\n\tboolean isComplex();\n\n\tObject custom();\n\n\tString example();\n\n\tString comment();\n\n\tboolean maybeCheck();\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/SimpleParamValidator.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\nimport org.yx.annotation.Bean;\nimport org.yx.common.locale.I18n;\nimport org.yx.conf.AppInfo;\n\n@Bean\npublic class SimpleParamValidator implements Validator {\n\n\t@Override\n\tpublic void valid(ParameterInfo info, Object arg) throws InvalidParamException {\n\t\tif (info.isRequired()) {\n\t\t\tif (arg == null || \"\".equals(arg)) {\n\t\t\t\tthrow new InvalidParamException(\n\t\t\t\t\t\tAppInfo.get(\"sumk.valid.msg.null\", InvalidParamException.PARAM_SLOT + \"不能为空\"), info);\n\t\t\t}\n\t\t}\n\t\tif (arg == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tString msg = buildMaxMessage(info.getMax(), arg);\n\t\tif (msg != null) {\n\t\t\tthrow new InvalidParamException(msg, info);\n\t\t}\n\n\t\tmsg = buildMinMessage(info.getMin(), arg);\n\t\tif (msg != null) {\n\t\t\tthrow new InvalidParamException(msg, info);\n\t\t}\n\t}\n\n\tpublic static String buildMaxMessage(int expect, Object arg) {\n\t\tif (expect < 0) {\n\t\t\treturn null;\n\t\t}\n\t\tClass<?> clz = arg.getClass();\n\t\tif (String.class == clz) {\n\t\t\tString s = (String) arg;\n\t\t\tif (s.length() > expect) {\n\t\t\t\treturn I18n.get(\"sumk.valid.msg.maxLength\", InvalidParamException.PARAM_SLOT + \"的长度最大{0},实际却是{1}\",\n\t\t\t\t\t\texpect, s.length());\n\t\t\t}\n\t\t}\n\t\tif (Number.class.isAssignableFrom(clz)) {\n\t\t\tlong n = ((Number) arg).longValue();\n\t\t\tif (n > expect) {\n\t\t\t\treturn I18n.get(\"sumk.valid.msg.max\", InvalidParamException.PARAM_SLOT + \"的值最大{0},实际却是{1}\", expect,\n\t\t\t\t\t\targ);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static String buildMinMessage(int expect, Object arg) {\n\t\tif (expect < 0) {\n\t\t\treturn null;\n\t\t}\n\t\tClass<?> clz = arg.getClass();\n\t\tif (String.class == clz) {\n\t\t\tString s = (String) arg;\n\t\t\tif (s.length() < expect) {\n\t\t\t\treturn I18n.get(\"sumk.valid.msg.minLength\", InvalidParamException.PARAM_SLOT + \"的长度最小{0},实际却是{1}\",\n\t\t\t\t\t\texpect, s.length());\n\t\t\t}\n\t\t}\n\n\t\tif (Number.class.isAssignableFrom(clz)) {\n\t\t\tlong n = ((Number) arg).longValue();\n\t\t\tif (n < expect) {\n\t\t\t\treturn I18n.get(\"sumk.valid.msg.min\", InvalidParamException.PARAM_SLOT + \"的值最小{0},实际却是{1}\", expect,\n\t\t\t\t\t\targ);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/Validator.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\nimport org.yx.base.Ordered;\n\npublic interface Validator extends Ordered {\n\n\tvoid valid(ParameterInfo info, Object arg) throws InvalidParamException;\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/common/validate/Validators.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.common.validate;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.bean.IOC;\nimport org.yx.util.SumkDate;\n\npublic class Validators {\n\n\tprivate static Validator[] validators = new Validator[0];\n\n\tpublic static void check(ParameterInfo info, Object arg) throws InvalidParamException {\n\t\tif (info == null || !info.maybeCheck()) {\n\t\t\treturn;\n\t\t}\n\t\tValidator[] validators = Validators.validators;\n\t\tfor (Validator v : validators) {\n\t\t\tv.valid(info, arg);\n\t\t}\n\t}\n\n\tpublic static synchronized void init() {\n\t\tif (validators.length > 0) {\n\t\t\treturn;\n\t\t}\n\t\tList<Validator> list = IOC.getBeans(Validator.class);\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tvalidators = list.toArray(new Validator[list.size()]);\n\t}\n\n\tpublic static boolean supportComplex(Class<?> clazz) {\n\t\tif (clazz.isArray()) {\n\t\t\treturn supportComplex(clazz.getComponentType());\n\t\t}\n\t\tif (clazz.isPrimitive() || clazz.getName().startsWith(\"java.\") || clazz == SumkDate.class\n\t\t\t\t|| Map.class.isAssignableFrom(clazz) || Collection.class.isAssignableFrom(clazz)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/main/StartConstants.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.main;\n\npublic abstract class StartConstants {\n\n\tpublic static final String INNER_PACKAGE = String.join(\".\", \"o\" + \"r\" + \"g\", \"y\" + \"x\");\n\tpublic static final String IOC_PACKAGES = \"sumk.ioc\";\n\n\tpublic static final String NOSOA = \"nosoa\";\n\tpublic static final String NOSOA_ClIENT = \"nosoaClient\";\n\tpublic static final String NOHTTP = \"nohttp\";\n\tpublic static final String EMBED_WEBSERVER_DISABLE = \"sumk.webserver.disable\";\n\tpublic static final String THREAD_ON_DEAMON = \"daemon\";\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/main/SumkServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.main;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport org.yx.base.context.AppContext;\nimport org.yx.bean.Booter;\nimport org.yx.bean.IOC;\nimport org.yx.bean.InnerIOC;\nimport org.yx.bean.Plugin;\nimport org.yx.bean.aop.asm.AsmUtils;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.conf.SystemConfig;\nimport org.yx.conf.SystemConfigHolder;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.log.RawLog;\nimport org.yx.redis.RedisPool;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\nimport org.yx.util.SumkThreadPool;\n\npublic final class SumkServer {\n\n\tprivate static boolean httpEnable;\n\tprivate static boolean rpcEnable;\n\n\tprivate static long startTime;\n\n\tpublic static long startTime() {\n\t\treturn startTime;\n\t}\n\n\t/**\n\t * 如果使用BootWatcher来加载远程配置，需要在BootWatcher里调用本方法\n\t */\n\tpublic static void reloadConfig() {\n\t\tAppContext sc = AppContext.inst();\n\t\tsc.setTest(AppInfo.getBoolean(\"sumk.test\", false));\n\t\tSumkServer.rpcEnable = sc.get(StartConstants.NOSOA) == null && soaPort() >= 0;\n\t\tSumkServer.httpEnable = sc.get(StartConstants.NOHTTP) == null && httpPort() > 0;\n\t}\n\n\tpublic static boolean isHttpEnable() {\n\t\treturn httpEnable;\n\t}\n\n\tpublic static boolean isRpcEnable() {\n\t\treturn rpcEnable;\n\t}\n\n\tpublic static void main(String[] args) {\n\t\trun(null, args);\n\t}\n\n\tpublic static void run(Class<?> mainClazz, String[] args) {\n\t\tstart(mainClazz, args == null ? Collections.emptyList() : Arrays.asList(args));\n\t}\n\n\tpublic static void startAsTool(Class<?> mainClazz) {\n\t\tstart(mainClazz, Arrays.asList(StartConstants.NOHTTP, StartConstants.NOSOA, StartConstants.NOSOA_ClIENT));\n\t}\n\n\tpublic static synchronized void start(Class<?> mainClazz, SystemConfig config, Collection<String> args) {\n\t\tif (config != null) {\n\t\t\tSystemConfigHolder.setSystemConfig(config);\n\t\t}\n\t\tstart(mainClazz, args);\n\t}\n\n\t/**\n\t * 启动ioc框架\n\t * \n\t * @param mainClazz 类似于springboot的启动类，会扫描该类底下的类。支持为null\n\t * @param args      启动参数,可为null\n\t */\n\tpublic static synchronized void start(Class<?> mainClazz, Collection<String> args) {\n\t\tif (AppContext.inst().isStarted()) {\n\t\t\treturn;\n\t\t}\n\t\tstartTime = System.currentTimeMillis();\n\t\tAppContext.inst().setStatusToStarted();\n\t\tif (mainClazz != null && mainClazz.getClassLoader() != null) {\n\t\t\tLoader.setClassLoader(mainClazz.getClassLoader());\n\t\t}\n\t\tif (args == null) {\n\t\t\targs = Collections.emptyList();\n\t\t}\n\t\ttry {\n\t\t\thandleArgs(args);\n\t\t\tbeforeStart();\n\t\t\tAppContext sc = AppContext.inst();\n\t\t\tif (Logs.system().isDebugEnabled()) {\n\t\t\t\tLogs.system().debug(\"start contexts:{}\", sc);\n\t\t\t}\n\n\t\t\tif (sc.get(StartConstants.THREAD_ON_DEAMON) != null) {\n\t\t\t\tSumkThreadPool.setDaemon(true);\n\t\t\t}\n\t\t\treloadConfig();\n\t\t\tList<String> pcks = getIocScanPath(mainClazz);\n\t\t\tnew Booter().start(pcks);\n\t\t\tSumkThreadPool.scheduleThreadPoolMonitor();\n\t\t\tRawLog.setLogger(RawLog.SLF4J_LOG);\n\t\t\tAppContext.clear();\n\t\t} catch (Throwable e) {\n\t\t\tLog.printStack(\"sumk.error\", e);\n\t\t\ttry {\n\t\t\t\tThread.sleep(1000);\n\t\t\t} catch (InterruptedException e1) {\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t}\n\t\t\tAppContext.startFailed();\n\t\t}\n\t\tLogs.system().info(\"start finished in {} ms. sumk version {}\", System.currentTimeMillis() - startTime,\n\t\t\t\tConst.sumkVersion());\n\t}\n\n\tprivate static List<String> getIocScanPath(Class<?> mainClazz) {\n\t\tString ioc = AppInfo.getLatin(StartConstants.IOC_PACKAGES);\n\t\tList<String> pcks = StringUtil.isEmpty(ioc) ? Collections.emptyList()\n\t\t\t\t: StringUtil.splitAndTrim(ioc, Const.COMMA, Const.SEMICOLON);\n\t\tpcks = new ArrayList<>(pcks);\n\n\t\tif (mainClazz != null && mainClazz.getPackage() != null\n\t\t\t\t&& StringUtil.isNotEmpty(mainClazz.getPackage().getName())) {\n\t\t\tString pkName = mainClazz.getPackage().getName();\n\t\t\tfor (String defined : pcks) {\n\t\t\t\tif (defined.startsWith(pkName + \".\")) {\n\t\t\t\t\treturn pcks;\n\t\t\t\t}\n\t\t\t}\n\t\t\tpcks.add(pkName);\n\t\t}\n\t\treturn pcks;\n\t}\n\n\t/**\n\t * 有时候希望在start之前做一些装配工作，这个方法就是用于完成这个目的。<BR>\n\t * 通过sumk.start.before.class.xx=类名（必须实现Runnable接口）来配置\n\t */\n\tprivate static void beforeStart() {\n\t\tString prefix = \"sumk.start.before.class.\";\n\t\tMap<String, String> map = AppInfo.subMap(prefix);\n\t\tif (map == null || map.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tmap = new TreeMap<>(map);\n\t\tfor (String key : map.keySet()) {\n\t\t\tString clzName = map.get(key);\n\t\t\tif (StringUtil.isEmpty(clzName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tclzName = StringUtil.toLatin(clzName).trim();\n\t\t\t\tObject obj = Loader.newInstance(clzName);\n\t\t\t\tif (!(obj instanceof Runnable)) {\n\t\t\t\t\tLogs.ioc().warn(\"{}的处理类{}不是Runnable类型，被自动过滤掉\", prefix + key, clzName);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t((Runnable) obj).run();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLogs.ioc().error(e.getLocalizedMessage(), e);\n\t\t\t\tAppContext.startFailed();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void handleArgs(Collection<String> args) {\n\t\tif (args == null) {\n\t\t\treturn;\n\t\t}\n\t\tfor (String arg : args) {\n\t\t\tif (arg.contains(\"=\")) {\n\t\t\t\tString[] kv = arg.split(\"=\", 2);\n\t\t\t\tAppContext.inst().put(kv[0], kv[1]);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tAppContext.inst().put(arg, Boolean.TRUE);\n\t\t}\n\n\t}\n\n\tpublic static boolean stop() {\n\t\tsynchronized (SumkServer.class) {\n\t\t\tif (AppContext.inst().isDestoryed()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tAppContext.inst().setStatusToDestoryed();\n\t\t}\n\t\tLogs.system().warn(\"sumk server stoping...\");\n\t\tList<Plugin> lifes = IOC.getBeans(Plugin.class);\n\t\tif (lifes != null && lifes.size() > 0) {\n\t\t\tCollections.reverse(lifes);\n\t\t\tfor (Plugin b : lifes) {\n\t\t\t\ttry {\n\t\t\t\t\tb.stop();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.printStack(\"sumk.error\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tRedisPool.shutdown();\n\t\t} catch (Throwable e2) {\n\t\t}\n\t\tAsmUtils.clearProxyClassLoaders();\n\t\tInnerIOC.clear();\n\t\tLogs.system().info(\"sumk server stoped!!!\");\n\t\treturn true;\n\t}\n\n\tpublic static void destroy() {\n\t\tif (!stop()) {\n\t\t\treturn;\n\t\t}\n\t\tSumkThreadPool.shutdown();\n\t}\n\n\tpublic static Executor getExecutor(String name) {\n\t\tObject obj = AppContext.inst().get(name);\n\t\tif (obj == null || !ExecutorService.class.isInstance(obj)) {\n\t\t\treturn SumkThreadPool.executor();\n\t\t}\n\t\treturn (Executor) obj;\n\t}\n\n\tpublic static ThreadPoolExecutor getHttpExecutor() {\n\t\tObject obj = AppContext.inst().get(\"sumk.http.executor\");\n\t\tif (obj == null || !ThreadPoolExecutor.class.isInstance(obj)) {\n\t\t\treturn (ThreadPoolExecutor) SumkThreadPool.executor();\n\t\t}\n\t\treturn (ThreadPoolExecutor) obj;\n\t}\n\n\t/**\n\t * \n\t * @return 剩余的启动超时时间，以ms为单位\n\t */\n\tpublic static long startTimeout() {\n\t\treturn SumkServer.startTime() + AppInfo.getLong(\"sumk.start.timeout\", 1000L * 60 * 10)\n\t\t\t\t- System.currentTimeMillis();\n\t}\n\n\tpublic static String soaHost() {\n\t\treturn AppInfo.get(\"sumk.rpc.host\", AppInfo.getLocalIp());\n\t}\n\n\tpublic static int soaPort() {\n\t\treturn AppInfo.getInt(\"sumk.rpc.port\", -1);\n\t}\n\n\tpublic static String httpHost() {\n\t\treturn AppInfo.get(\"sumk.http.host\", null);\n\t}\n\n\tpublic static int httpPort() {\n\t\treturn AppInfo.getInt(\"sumk.http.port\", -1);\n\t}\n\n\t/**\n\t * 重置状态，一般用于单元测试\n\t */\n\tpublic static void resetStatus() {\n\t\tAppContext.inst().resetStatus();\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/Checkable.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\npublic interface Checkable {\n\n\tboolean aliveCheck();\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/Redis.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\nimport org.yx.redis.command.BinaryJedisCommand;\nimport org.yx.redis.command.JedisCommand;\nimport org.yx.redis.command.MultiKeyCommand;\nimport org.yx.redis.command.ScriptingCommand;\n\npublic interface Redis extends BinaryJedisCommand, JedisCommand, MultiKeyCommand, ScriptingCommand {\n\n\t/**\n\t * @return redis的主机地址，如果存在多个，就用逗号分隔\n\t */\n\tString hosts();\n\n\t/**\n\t * 返回的config只是用来查看，不要去修改它。对它的修改没有意义\n\t * \n\t * @return 构造redis对象所使用的配置\n\t */\n\tRedisConfig getRedisConfig();\n\n\tvoid shutdownPool();\n\n\tRedisType redisType();\n\n\t/**\n\t * 如果发生了异常，返回null代替抛出异常。 一般而言它只对普通redis起作用，对于redis集群，该方法不一定有作用。\n\t * 通过isMuted()可以判断是否生效\n\t * \n\t * @return Redis对象，不为null\n\t */\n\tRedis mute();\n\n\tboolean isMuted();\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/RedisChecker.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\nimport org.yx.common.util.helper.ArrayHelper;\nimport org.yx.log.Logs;\n\npublic final class RedisChecker implements Runnable {\n\n\tprivate static final RedisChecker holder = new RedisChecker();\n\n\tprivate RedisChecker() {\n\n\t}\n\n\tpublic static RedisChecker get() {\n\t\treturn holder;\n\t}\n\n\tprivate Checkable[] allRedis = new Checkable[0];\n\n\tpublic synchronized void addRedis(Checkable c) {\n\t\tLogs.redis().debug(\"add alive check: {}\", c);\n\t\tthis.allRedis = ArrayHelper.add(this.allRedis, c, Checkable.class);\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tCheckable[] rediss = this.allRedis;\n\t\tfor (Checkable redis : rediss) {\n\t\t\tredis.aliveCheck();\n\t\t}\n\t}\n\n\tpublic void remove(Checkable c) {\n\t\tLogs.redis().debug(\"remove alive chech: {}\", c);\n\t\tthis.allRedis = ArrayHelper.remove(this.allRedis, c, Checkable.class);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/RedisConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\nimport org.apache.commons.pool2.impl.GenericObjectPoolConfig;\nimport org.yx.exception.SumkException;\nimport org.yx.util.StringUtil;\n\n@SuppressWarnings(\"rawtypes\")\npublic class RedisConfig extends GenericObjectPoolConfig {\n\n\tprivate final String hosts;\n\tprivate int db = 0;\n\tprivate int connectionTimeout = 5000;\n\tprivate int timeout = 2000;\n\tprivate String user;\n\n\tpublic String getUser() {\n\t\treturn user;\n\t}\n\n\tpublic void setUser(String user) {\n\t\tthis.user = user;\n\t}\n\n\tprivate String password;\n\tprivate int maxAttempts = 3;\n\tprivate String master;\n\tprivate String alias;\n\n\t/**\n\t * 1 cluster 2 sentinel 其它是普通\n\t */\n\tprivate String type;\n\n\t@SuppressWarnings(\"deprecation\")\n\tpublic RedisConfig(String host) {\n\t\tif (host == null || host.isEmpty()) {\n\t\t\tthrow new SumkException(657645465, \"redis host cannot be empty\");\n\t\t}\n\t\tthis.hosts = StringUtil.toLatin(host.trim());\n\n\t\tsetTestWhileIdle(true);\n\t\tsetMinEvictableIdleTimeMillis(60000);\n\t\tsetTimeBetweenEvictionRunsMillis(30000);\n\t}\n\n\tpublic String getMaster() {\n\t\treturn master;\n\t}\n\n\tpublic void setMaster(String master) {\n\t\tthis.master = master;\n\t}\n\n\tpublic String hosts() {\n\t\treturn hosts;\n\t}\n\n\tpublic int getTimeout() {\n\t\treturn timeout;\n\t}\n\n\tpublic void setTimeout(int timeout) {\n\t\tthis.timeout = timeout;\n\t}\n\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\n\tpublic int getDb() {\n\t\treturn db;\n\t}\n\n\tpublic void setDb(int db) {\n\t\tthis.db = db;\n\t}\n\n\tpublic int getMaxAttempts() {\n\t\treturn maxAttempts;\n\t}\n\n\tpublic void setMaxAttempts(int maxAttempts) {\n\t\tthis.maxAttempts = maxAttempts;\n\t}\n\n\tpublic String getAlias() {\n\t\treturn alias;\n\t}\n\n\tpublic void setAlias(String alias) {\n\t\tthis.alias = alias;\n\t}\n\n\tpublic int getConnectionTimeout() {\n\t\treturn connectionTimeout;\n\t}\n\n\tpublic void setConnectionTimeout(int connectionTimeout) {\n\t\tthis.connectionTimeout = connectionTimeout;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"hosts=\" + hosts + \", db=\" + db + \", user=\" + user + \", password=\" + password + \", timeout=\" + timeout\n\t\t\t\t+ \", maxAttempts=\" + maxAttempts + \" , \" + super.toString();\n\t}\n\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(String type) {\n\t\tthis.type = type;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/RedisCounter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\nimport org.yx.common.sequence.SeqCounter;\n\npublic final class RedisCounter implements SeqCounter {\n\n\tprivate final Redis redis;\n\n\t@Override\n\tpublic int incr(String name) {\n\t\tif (name == null || name.isEmpty()) {\n\t\t\treturn redis.incr(\"__SEQ_GLOBAL_FOR_ALL\").intValue();\n\t\t}\n\t\tRedis r = RedisPool.getRedisExactly(name);\n\t\tif (r == null) {\n\t\t\tr = this.redis;\n\t\t}\n\t\treturn r.incr(name).intValue();\n\t}\n\n\tpublic RedisCounter(Redis redis) {\n\t\tthis.redis = redis;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/RedisFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\nimport org.yx.base.Ordered;\n\npublic interface RedisFactory extends Ordered {\n\n\tRedis create(RedisConfig config);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/RedisLoader.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.yx.bean.IOC;\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.SimpleBeanUtil;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic class RedisLoader {\n\n\tpublic static final String SEQ = \"seq\";\n\tpublic static final String SESSION = \"session\";\n\n\tprivate static final String CONFIG_PREFIX = \"s.redis.\";\n\n\tpublic static synchronized void init() throws Exception {\n\t\tMap<String, String> map = AppInfo.subMap(CONFIG_PREFIX);\n\t\tif (map == null || map.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tMap<String, String> commonConfig = Collections.unmodifiableMap(AppInfo.subMap(\"s.common.redis.\"));\n\t\tfor (String key : map.keySet()) {\n\t\t\tif (key.contains(\".\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tMap<String, String> configMap = new HashMap<>(commonConfig);\n\t\t\tconfigMap.putAll(CollectionUtil.subMap(map, key + \".\"));\n\t\t\tinitRedis(key, StringUtil.toLatin(map.get(key).trim()), configMap);\n\t\t}\n\t\tif (Logs.redis().isDebugEnabled()) {\n\t\t\tLogs.redis().debug(\"redis的主键列表{},默认redis: {}\", RedisPool.names(), RedisPool.defaultRedis());\n\t\t}\n\t}\n\n\tpublic static void initRedis(final String name, String host, Map<String, String> configMap) {\n\t\tLogs.redis().info(\"开始初始化redis：{}={}\", name, host);\n\t\ttry {\n\t\t\tRedisConfig config = createConfig(host, configMap);\n\t\t\tLogs.redis().debug(\"{} : {}\", name, config);\n\n\t\t\tRedis redis = IOC.getFirstBean(RedisFactory.class, false).create(config);\n\t\t\tif (\"*\".equals(name) || \"default\".equals(name)) {\n\t\t\t\tRedisPool.setDefaultRedis(redis);\n\t\t\t} else {\n\t\t\t\tRedisPool.putIfAbsent(name, redis);\n\t\t\t}\n\t\t\tString aliases = config.getAlias();\n\t\t\tif (StringUtil.isNotEmpty(aliases)) {\n\t\t\t\taliases = StringUtil.toLatin(aliases);\n\t\t\t\tfor (String s : StringUtil.splitAndTrim(aliases, \",\")) {\n\t\t\t\t\tif (RedisPool.getRedisExactly(s) != null) {\n\t\t\t\t\t\tLogs.redis().warn(\"{}的redis配置已经存在了\", s);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tLogs.redis().debug(\"设置别名{} -> {}\", s, name);\n\t\t\t\t\tRedisPool.putIfAbsent(s, redis);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new SumkException(35345, \"redis [\" + name + \"] 初始化失败\", e);\n\t\t}\n\n\t}\n\n\tpublic static RedisConfig createConfig(String host, Map<String, String> configMap) {\n\t\tRedisConfig config = new RedisConfig(host);\n\t\tif (configMap != null && configMap.size() > 0) {\n\t\t\ttry {\n\t\t\t\tSimpleBeanUtil.copyProperties(config, configMap);\n\t\t\t\tif (AppInfo.getBoolean(\"sumk.redis.password.encry\", false)\n\t\t\t\t\t\t&& StringUtil.isNotEmpty(config.getPassword())) {\n\t\t\t\t\tbyte[] bs = S.base64().decode(config.getPassword().getBytes());\n\t\t\t\t\tString p2 = new String(S.cipher().decrypt(bs, RedisSettings.getPasswordKey()));\n\t\t\t\t\tconfig.setPassword(p2);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLogs.redis().error(e.getLocalizedMessage(), e);\n\t\t\t}\n\t\t}\n\t\treturn config;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/RedisPlugin.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\nimport java.util.concurrent.TimeUnit;\n\nimport org.yx.annotation.Bean;\nimport org.yx.bean.Plugin;\nimport org.yx.common.lock.Locker;\nimport org.yx.common.sequence.SeqHolder;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.util.Loader;\nimport org.yx.util.Task;\n\n@Bean\npublic class RedisPlugin implements Plugin {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 50;\n\t}\n\n\t@Override\n\tpublic void prepare() {\n\t\tif (AppInfo.subMap(\"s.redis.\").isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tLoader.loadClass(\"redis.clients.jedis.Jedis\");\n\t\t} catch (Throwable e) {\n\t\t\tLogs.redis().warn(\"Jedis is not in use because of \" + e.getMessage());\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tLogs.redis().debug(\"redis pool init\");\n\t\t\tRedisLoader.init();\n\t\t\tinitSeqUtilCounter();\n\t\t\tTask.scheduleAtFixedRate(RedisChecker.get(), 5, AppInfo.getInt(\"sumk.redis.check.period\", 5),\n\t\t\t\t\tTimeUnit.SECONDS);\n\t\t} catch (Exception e) {\n\t\t\tLogs.redis().error(e.getMessage(), e);\n\t\t\tthrow new SumkException(35345436, \"redis启动失败\");\n\t\t}\n\t\tLocker.init();\n\t}\n\n\t@Override\n\tpublic void startAsync() {\n\t}\n\n\tprivate static void initSeqUtilCounter() {\n\t\tif (SeqHolder.inst().getCounter() != null) {\n\t\t\treturn;\n\t\t}\n\t\tRedis redis = RedisPool.getRedisExactly(AppInfo.get(\"sumk.counter.name\", RedisLoader.SEQ));\n\t\tif (redis == null) {\n\t\t\tredis = RedisPool.getRedisExactly(\"session\");\n\t\t}\n\t\tif (redis != null) {\n\t\t\tLogs.redis().debug(\"use redis counter\");\n\t\t\tSeqHolder.inst().setCounter(new RedisCounter(redis));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/RedisPool.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.yx.log.Logs;\n\npublic final class RedisPool {\n\n\tprivate static Map<String, Redis> cache = Collections.emptyMap();\n\n\tprivate static Redis _defaultRedis;\n\n\tstatic void setDefaultRedis(Redis r) {\n\t\t_defaultRedis = r;\n\t}\n\n\tpublic static List<String> names() {\n\t\treturn new ArrayList<String>(cache.keySet());\n\t}\n\n\tpublic static Redis get(String alias) {\n\t\tif (alias == null) {\n\t\t\treturn _defaultRedis;\n\t\t}\n\t\treturn cache.getOrDefault(alias, _defaultRedis);\n\t}\n\n\tpublic static Redis getRedisExactly(String alias) {\n\t\treturn cache.get(alias);\n\t}\n\n\tpublic static Redis defaultRedis() {\n\t\treturn _defaultRedis;\n\t}\n\n\tpublic static synchronized Redis put(String alias, Redis redis) {\n\t\tMap<String, Redis> map = new HashMap<>(cache);\n\t\tRedis old = map.put(alias, Objects.requireNonNull(redis));\n\t\tcache = map;\n\t\tLogs.redis().info(\"redis name replace to {} : {}\", alias, redis);\n\t\treturn old;\n\t}\n\n\tpublic static synchronized boolean putIfAbsent(String alias, Redis redis) {\n\t\tMap<String, Redis> map = new HashMap<>(cache);\n\t\tRedis old = map.putIfAbsent(alias, Objects.requireNonNull(redis));\n\t\tif (old == null) {\n\t\t\tcache = map;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static synchronized Redis remove(String alias) {\n\t\tMap<String, Redis> map = new HashMap<>(cache);\n\t\tRedis old = map.remove(alias);\n\t\tif (old != null) {\n\t\t\tcache = map;\n\t\t}\n\t\treturn old;\n\t}\n\n\tpublic static void shutdown() {\n\t\tSet<Redis> redises = new HashSet<>(cache.values());\n\t\tif (_defaultRedis != null) {\n\t\t\tredises.add(_defaultRedis);\n\t\t}\n\n\t\tfor (Redis r : redises) {\n\t\t\tr.shutdownPool();\n\t\t}\n\t\tcache = Collections.emptyMap();\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/RedisSettings.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.yx.common.Host;\nimport org.yx.util.StringUtil;\n\npublic final class RedisSettings {\n\tprivate static byte[] PASSWORD_KEY = new byte[] { 121, 111, 117, 116, 111, 110, 103, 108, 117, 97, 110, 64, 115,\n\t\t\t117, 109, 107 };\n\n\tpublic static byte[] getPasswordKey() {\n\t\treturn PASSWORD_KEY;\n\t}\n\n\tpublic static void setPasswordKey(byte[] passwordKey) {\n\t\tPASSWORD_KEY = Objects.requireNonNull(passwordKey);\n\t}\n\n\tpublic static List<Host> parseHosts(String host) {\n\t\tString h = StringUtil.toLatin(host).replaceAll(\"\\\\s\", \"\");\n\t\tString[] hs = h.split(\",\");\n\t\tList<Host> hosts = new ArrayList<>(hs.length);\n\t\tfor (String addr : hs) {\n\t\t\tif (addr.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!addr.contains(\":\")) {\n\t\t\t\thosts.add(Host.create(addr, 6379));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\thosts.add(Host.create(addr));\n\t\t}\n\t\treturn hosts;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/RedisType.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis;\n\npublic enum RedisType {\n\tSIMPLE, CLUSTER,\n\t/**\n\t * 实验性功能\n\t */\n\tSENTINEL, OTHER;\n\n\tpublic boolean accept(String type) {\n\t\tif (type == null || type.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this.name().equalsIgnoreCase(type)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (String.valueOf(this.ordinal()).equals(type)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/command/BinaryJedisCommand.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.command;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic interface BinaryJedisCommand {\n\tString set(byte[] key, byte[] value);\n\n\tString set(byte[] key, byte[] value, byte[] nxxx, byte[] expx, long time);\n\n\tbyte[] get(byte[] key);\n\n\tBoolean exists(byte[] key);\n\n\tString type(byte[] key);\n\n\tLong pexpire(byte[] key, long milliseconds);\n\n\tLong pttl(byte[] key);\n\n\tLong setrange(byte[] key, long offset, byte[] value);\n\n\tbyte[] getrange(byte[] key, long startOffset, long endOffset);\n\n\tLong hset(byte[] key, byte[] field, byte[] value);\n\n\tLong hset(byte[] key, Map<byte[], byte[]> hash);\n\n\tbyte[] hget(byte[] key, byte[] field);\n\n\tLong hsetnx(byte[] key, byte[] field, byte[] value);\n\n\tString hmset(byte[] key, Map<byte[], byte[]> hash);\n\n\tList<byte[]> hmget(byte[] key, byte[]... fields);\n\n\tBoolean hexists(byte[] key, byte[] field);\n\n\tLong hdel(byte[] key, byte[]... field);\n\n\tSet<byte[]> hkeys(byte[] key);\n\n\tCollection<byte[]> hvals(byte[] key);\n\n\tMap<byte[], byte[]> hgetAll(byte[] key);\n\n\tLong rpush(byte[] key, byte[]... args);\n\n\tLong lpush(byte[] key, byte[]... args);\n\n\tLong llen(byte[] key);\n\n\tList<byte[]> lrange(byte[] key, long start, long stop);\n\n\tString ltrim(byte[] key, long start, long stop);\n\n\tbyte[] lindex(byte[] key, long index);\n\n\tString lset(byte[] key, long index, byte[] value);\n\n\tLong lrem(byte[] key, long count, byte[] value);\n\n\tbyte[] lpop(byte[] key);\n\n\tbyte[] rpop(byte[] key);\n\n\tLong del(byte[] key);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/command/JedisCommand.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.command;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic interface JedisCommand {\n\tString set(String key, String value);\n\n\tString set(String key, String value, String nxxx, String expx, long time);\n\n\tString set(String key, String value, String expx, long time);\n\n\tString set(String key, String value, String nxxx);\n\n\tString get(String key);\n\n\tBoolean exists(String key);\n\n\tLong persist(String key);\n\n\tString type(String key);\n\n\tbyte[] dump(String key);\n\n\tString restore(String key, int ttl, byte[] serializedValue);\n\n\tLong expire(String key, int seconds);\n\n\tLong pexpire(String key, long milliseconds);\n\n\tLong expireAt(String key, long unixTime);\n\n\tLong pexpireAt(String key, long millisecondsTimestamp);\n\n\tLong ttl(String key);\n\n\tLong pttl(String key);\n\n\tLong touch(String key);\n\n\tBoolean setbit(String key, long offset, boolean value);\n\n\tBoolean getbit(String key, long offset);\n\n\tLong setrange(String key, long offset, String value);\n\n\tString getrange(String key, long startOffset, long endOffset);\n\n\tString getSet(String key, String value);\n\n\tLong setnx(String key, String value);\n\n\tString setex(String key, int seconds, String value);\n\n\tString psetex(String key, long milliseconds, String value);\n\n\tLong decrBy(String key, long decrement);\n\n\tLong decr(String key);\n\n\tLong incrBy(String key, long increment);\n\n\tDouble incrByFloat(String key, double increment);\n\n\tLong incr(String key);\n\n\tLong append(String key, String value);\n\n\tString substr(String key, int start, int end);\n\n\tLong hset(String key, String field, String value);\n\n\tLong hset(String key, Map<String, String> hash);\n\n\tString hget(String key, String field);\n\n\tLong hsetnx(String key, String field, String value);\n\n\tString hmset(String key, Map<String, String> hash);\n\n\tList<String> hmget(String key, String... fields);\n\n\tLong hincrBy(String key, String field, long value);\n\n\tDouble hincrByFloat(String key, String field, double value);\n\n\tBoolean hexists(String key, String field);\n\n\tLong hdel(String key, String... field);\n\n\tLong hlen(String key);\n\n\tSet<String> hkeys(String key);\n\n\tList<String> hvals(String key);\n\n\tMap<String, String> hgetAll(String key);\n\n\tLong rpush(String key, String... string);\n\n\tLong lpush(String key, String... string);\n\n\tLong llen(String key);\n\n\tList<String> lrange(String key, long start, long stop);\n\n\tString ltrim(String key, long start, long stop);\n\n\tString lindex(String key, long index);\n\n\tString lset(String key, long index, String value);\n\n\tLong lrem(String key, long count, String value);\n\n\tString lpop(String key);\n\n\tString rpop(String key);\n\n\tLong sadd(String key, String... member);\n\n\tSet<String> smembers(String key);\n\n\tLong srem(String key, String... member);\n\n\tString spop(String key);\n\n\tSet<String> spop(String key, long count);\n\n\tLong scard(String key);\n\n\tBoolean sismember(String key, String member);\n\n\tString srandmember(String key);\n\n\tList<String> srandmember(String key, int count);\n\n\tLong strlen(String key);\n\n\tLong zadd(String key, double score, String member);\n\n\tLong zadd(String key, Map<String, Double> scoreMembers);\n\n\tSet<String> zrange(String key, long start, long stop);\n\n\tLong zrem(String key, String... members);\n\n\tDouble zincrby(String key, double increment, String member);\n\n\tLong zrank(String key, String member);\n\n\tLong zrevrank(String key, String member);\n\n\tSet<String> zrevrange(String key, long start, long stop);\n\n\tLong zcard(String key);\n\n\tDouble zscore(String key, String member);\n\n\tList<String> sort(String key);\n\n\tLong zcount(String key, double min, double max);\n\n\tLong zcount(String key, String min, String max);\n\n\tSet<String> zrangeByScore(String key, double min, double max);\n\n\tSet<String> zrangeByScore(String key, String min, String max);\n\n\tSet<String> zrevrangeByScore(String key, double max, double min);\n\n\tSet<String> zrangeByScore(String key, double min, double max, int offset, int count);\n\n\tSet<String> zrevrangeByScore(String key, String max, String min);\n\n\tSet<String> zrangeByScore(String key, String min, String max, int offset, int count);\n\n\tSet<String> zrevrangeByScore(String key, double max, double min, int offset, int count);\n\n\tSet<String> zrevrangeByScore(String key, String max, String min, int offset, int count);\n\n\tLong zremrangeByRank(String key, long start, long stop);\n\n\tLong zremrangeByScore(String key, double min, double max);\n\n\tLong zremrangeByScore(String key, String min, String max);\n\n\tLong zlexcount(String key, String min, String max);\n\n\tSet<String> zrangeByLex(String key, String min, String max);\n\n\tSet<String> zrangeByLex(String key, String min, String max, int offset, int count);\n\n\tSet<String> zrevrangeByLex(String key, String max, String min);\n\n\tSet<String> zrevrangeByLex(String key, String max, String min, int offset, int count);\n\n\tLong zremrangeByLex(String key, String min, String max);\n\n\tLong lpushx(String key, String... string);\n\n\tLong rpushx(String key, String... string);\n\n\tList<String> blpop(int timeout, String key);\n\n\tList<String> brpop(int timeout, String key);\n\n\tLong del(String key);\n\n\tLong unlink(String key);\n\n\tString echo(String string);\n\n\tLong move(String key, int dbIndex);\n\n\tLong bitcount(String key);\n\n\tLong bitcount(String key, long start, long end);\n\n\tLong pfadd(String key, String... elements);\n\n\tlong pfcount(String key);\n\n\tLong geoadd(String key, double longitude, double latitude, String member);\n\n\tDouble geodist(String key, String member1, String member2);\n\n\tList<String> geohash(String key, String... members);\n\n\tList<Long> bitfield(String key, String... arguments);\n\n\tLong hstrlen(String key, String field);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/command/MultiKeyCommand.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.command;\n\nimport java.util.List;\nimport java.util.Set;\n\npublic interface MultiKeyCommand {\n\tLong del(String... keys);\n\n\tLong unlink(String... keys);\n\n\tLong exists(String... keys);\n\n\tList<String> blpop(int timeout, String... keys);\n\n\tList<String> brpop(int timeout, String... keys);\n\n\tList<String> blpop(String... args);\n\n\tList<String> brpop(String... args);\n\n\tSet<String> keys(String pattern);\n\n\tList<String> mget(String... keys);\n\n\tString mset(String... keysvalues);\n\n\tLong msetnx(String... keysvalues);\n\n\tString rename(String oldkey, String newkey);\n\n\tLong renamenx(String oldkey, String newkey);\n\n\tString rpoplpush(String srckey, String dstkey);\n\n\tSet<String> sdiff(String... keys);\n\n\tLong sdiffstore(String dstkey, String... keys);\n\n\tSet<String> sinter(String... keys);\n\n\tLong sinterstore(String dstkey, String... keys);\n\n\tLong smove(String srckey, String dstkey, String member);\n\n\tLong sort(String key, String dstkey);\n\n\tSet<String> sunion(String... keys);\n\n\tLong sunionstore(String dstkey, String... keys);\n\n\tString watch(String... keys);\n\n\tString unwatch();\n\n\tLong zinterstore(String dstkey, String... sets);\n\n\tLong zunionstore(String dstkey, String... sets);\n\n\tString brpoplpush(String source, String destination, int timeout);\n\n\tLong publish(String channel, String message);\n\n\tString randomKey();\n\n\tString pfmerge(String destkey, String... sourcekeys);\n\n\tlong pfcount(String... keys);\n\n\tLong touch(String... keys);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/command/ScriptingCommand.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.command;\n\nimport java.util.List;\n\npublic interface ScriptingCommand {\n\tObject eval(String script, int keyCount, String... params);\n\n\tObject eval(String script, List<String> keys, List<String> args);\n\n\tObject eval(String script, String sampleKey);\n\n\tObject evalsha(String sha1, String sampleKey);\n\n\tObject evalsha(String sha1, List<String> keys, List<String> args);\n\n\tObject evalsha(String sha1, int keyCount, String... params);\n\n\tString scriptLoad(String script);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/v3/AbstractJedisExecutor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.v3;\n\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport org.yx.exception.SimpleSumkException;\nimport org.yx.exception.SumkException;\nimport org.yx.exception.SumkExceptionCode;\nimport org.yx.log.Logs;\nimport org.yx.redis.RedisConfig;\n\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.exceptions.JedisConnectionException;\nimport redis.clients.jedis.util.Pool;\n\npublic abstract class AbstractJedisExecutor implements JedisExecutor {\n\n\tprivate static final SumkException DIS_CONNECTION_EXCEPTION = new SimpleSumkException(\n\t\t\tSumkExceptionCode.REDIS_DIS_CONNECTION, \"redis is disConnected\");\n\n\tprotected boolean disConnected;\n\tprotected final RedisConfig config;\n\tprotected final Pool<Jedis> pool;\n\n\tpublic AbstractJedisExecutor(RedisConfig config, Pool<Jedis> pool) {\n\t\tthis.config = Objects.requireNonNull(config);\n\t\tthis.pool = Objects.requireNonNull(pool);\n\t}\n\n\tpublic Jedis jedis() {\n\t\treturn pool.getResource();\n\t}\n\n\t@Override\n\tpublic RedisConfig getRedisConfig() {\n\t\treturn this.config;\n\t}\n\n\t@Override\n\tpublic String hosts() {\n\t\treturn config.hosts();\n\t}\n\n\tprotected boolean isConnectException(Throwable e) {\n\t\treturn e instanceof JedisConnectionException\n\t\t\t\t|| (e.getCause() != null && e.getCause() instanceof JedisConnectionException);\n\t}\n\n\t@Override\n\tpublic <T> T execAndRetry(Function<Jedis, T> callback, boolean mute) {\n\t\treturn this.exec(callback, this.config.getMaxAttempts(), mute);\n\t}\n\n\tpublic <T> T exec(Function<Jedis, T> callback, int maxAttempts, boolean mute) {\n\t\tif (maxAttempts < 1) {\n\t\t\tmaxAttempts = 1;\n\t\t}\n\t\tJedis jedis = null;\n\t\tThrowable e1 = null;\n\t\tfor (int i = 0; i < maxAttempts; i++) {\n\t\t\tif (this.disConnected) {\n\t\t\t\tif (mute) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tthrow DIS_CONNECTION_EXCEPTION;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tjedis = this.jedis();\n\t\t\t\treturn callback.apply(jedis);\n\t\t\t} catch (Throwable e) {\n\t\t\t\te1 = e;\n\t\t\t\tif (isConnectException(e)) {\n\t\t\t\t\tLogs.redis().warn(\"redis连接异常！({}){}\", hosts(), e.getMessage());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLogs.redis().error(\"redis执行错误！({}){}\", hosts(), e.getMessage());\n\t\t\t\tbreak;\n\t\t\t} finally {\n\t\t\t\tif (jedis != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tjedis.close();\n\t\t\t\t\t} catch (Throwable e2) {\n\t\t\t\t\t\tLogs.redis().error(e2.toString(), e2);\n\t\t\t\t\t}\n\t\t\t\t\tjedis = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (mute) {\n\t\t\treturn null;\n\t\t}\n\t\tif (e1 != null) {\n\t\t\tthrow new SumkException(12342422, e1.getMessage(), e1);\n\t\t}\n\t\tthrow new SumkException(12342423, \"未知redis异常,执行次数:\" + maxAttempts);\n\t}\n\n\tpublic void shutdownPool() {\n\t\tthis.pool.close();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder(32);\n\t\tsb.append(\"[hosts=\").append(hosts()).append(\",db=\").append(config.getDb()).append(\",maxAttempts=\")\n\t\t\t\t.append(config.getMaxAttempts());\n\t\ttry {\n\t\t\tsb.append(\",idle=\").append(pool.getNumIdle()).append(\",active=\").append(pool.getNumActive());\n\t\t} catch (Throwable e) {\n\n\t\t}\n\t\treturn sb.append(\"]\").toString();\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/v3/AbstractRedis.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.v3;\n\nimport java.util.function.Function;\n\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.redis.Checkable;\nimport org.yx.redis.Redis;\nimport org.yx.redis.RedisChecker;\nimport org.yx.redis.RedisConfig;\nimport org.yx.redis.RedisType;\n\nimport redis.clients.jedis.Jedis;\n\npublic abstract class AbstractRedis implements Redis, Redis3x, Cloneable {\n\n\tprotected final JedisExecutor jedis2Executor;\n\tprivate boolean mute;\n\tprivate transient AbstractRedis muteConnectionExceptionRedis;\n\n\tpublic AbstractRedis(JedisExecutor executor) {\n\t\tthis.jedis2Executor = executor;\n\t\tif (this.jedis2Executor instanceof Checkable) {\n\t\t\tRedisChecker.get().addRedis((Checkable) this.jedis2Executor);\n\t\t}\n\t}\n\n\t@Override\n\tpublic RedisConfig getRedisConfig() {\n\t\treturn this.jedis2Executor.getRedisConfig();\n\t}\n\n\t@Override\n\tpublic String hosts() {\n\t\treturn jedis2Executor.hosts();\n\t}\n\n\t@Override\n\tpublic <T> T execute(String key, Function<Jedis, T> callback) {\n\t\treturn this.execAndRetry(callback);\n\t}\n\n\tpublic <T> T execAndRetry(Function<Jedis, T> callback) {\n\t\treturn this.jedis2Executor.execAndRetry(callback, mute);\n\t}\n\n\tpublic void shutdownPool() {\n\t\tthis.jedis2Executor.shutdownPool();\n\t\tif (this.jedis2Executor instanceof Checkable) {\n\t\t\tRedisChecker.get().remove((Checkable) this.jedis2Executor);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Redis mute() {\n\t\tAbstractRedis r = this.muteConnectionExceptionRedis;\n\t\tif (r != null) {\n\t\t\treturn r;\n\t\t}\n\t\tif (this.mute) {\n\t\t\treturn this;\n\t\t}\n\t\ttry {\n\t\t\tr = (AbstractRedis) super.clone();\n\t\t} catch (Exception e) {\n\t\t\tLogs.redis().error(e.toString(), e);\n\t\t\tthrow new SumkException(345345, this.getClass().getName() + \"无法clone\");\n\t\t}\n\t\tr.mute = true;\n\t\tthis.muteConnectionExceptionRedis = r;\n\t\treturn r;\n\t}\n\n\t@Override\n\tpublic boolean isMuted() {\n\t\treturn mute;\n\t}\n\n\t@Override\n\tpublic RedisType redisType() {\n\t\treturn this.jedis2Executor.redisType();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Redis[\" + this.jedis2Executor + \"]\";\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/v3/JedisExecutor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.v3;\n\nimport java.util.function.Function;\n\nimport org.yx.redis.RedisConfig;\nimport org.yx.redis.RedisType;\n\nimport redis.clients.jedis.Jedis;\n\npublic interface JedisExecutor {\n\n\t<T> T execAndRetry(Function<Jedis, T> callback, boolean mute);\n\n\tvoid shutdownPool();\n\n\tRedisConfig getRedisConfig();\n\n\tString hosts();\n\n\tRedisType redisType();\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/v3/Redis3Factory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.v3;\n\nimport org.yx.annotation.Bean;\nimport org.yx.base.Ordered;\nimport org.yx.log.Logs;\nimport org.yx.redis.Redis;\nimport org.yx.redis.RedisConfig;\nimport org.yx.redis.RedisFactory;\nimport org.yx.redis.RedisType;\n\n@Bean\npublic class Redis3Factory implements RedisFactory {\n\n\t@Override\n\tpublic Redis create(RedisConfig conf) {\n\t\tString type = conf.getType();\n\t\tif (RedisType.SENTINEL.accept(type)) {\n\t\t\tLogs.redis().warn(\"sentinel has not be tested.if any problem,send email to 3205207767@qq.com\");\n\t\t\treturn new RedisImpl(new SentinelJedisExecutor(conf, Redis3Kit.createSentinelPool(conf)));\n\t\t}\n\t\tif (RedisType.CLUSTER.accept(type) || conf.hosts().contains(\",\")) {\n\t\t\treturn Redis3Kit.createJedisCluster(conf);\n\t\t}\n\t\treturn new RedisImpl(new SingleJedisExecutor(conf, Redis3Kit.createJedisPool(conf)));\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn Ordered.DEFAULT_ORDER + 100;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/v3/Redis3Kit.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.v3;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.yx.common.Host;\nimport org.yx.conf.AppInfo;\n\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.redis.RedisConfig;\nimport org.yx.redis.RedisSettings;\n\nimport redis.clients.jedis.HostAndPort;\nimport redis.clients.jedis.JedisPool;\nimport redis.clients.jedis.JedisSentinelPool;\nimport redis.clients.jedis.params.SetParams;\n\npublic class Redis3Kit {\n\n\tpublic static SetParams createSetParams(String nxxx, String expx, long time) {\n\t\tSetParams params = SetParams.setParams();\n\t\tif (nxxx != null) {\n\t\t\tif (\"NX\".equalsIgnoreCase(nxxx)) {\n\t\t\t\tparams.nx();\n\t\t\t} else if (\"XX\".equalsIgnoreCase(nxxx)) {\n\t\t\t\tparams.xx();\n\t\t\t} else {\n\t\t\t\tthrow new SumkException(234325, \"nxxx参数错误：\" + nxxx);\n\t\t\t}\n\t\t}\n\t\tif (expx != null) {\n\t\t\tif (\"EX\".equalsIgnoreCase(expx)) {\n\t\t\t\tparams.ex(time);\n\t\t\t} else if (\"PX\".equalsIgnoreCase(expx)) {\n\t\t\t\tparams.px(time);\n\t\t\t} else {\n\t\t\t\tthrow new SumkException(234325, \"expx参数错误：\" + expx);\n\t\t\t}\n\t\t}\n\t\treturn params;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static JedisPool createJedisPool(RedisConfig conf) {\n\t\tList<Host> hosts = RedisSettings.parseHosts(conf.hosts());\n\t\tHost h = hosts.get(0);\n\t\ttry {\n\n\t\t\treturn new JedisPool(conf, h.ip(), h.port(), conf.getConnectionTimeout(), conf.getTimeout(), conf.getUser(),\n\t\t\t\t\tconf.getPassword(), conf.getDb(), AppInfo.appId(\"sumk\"));\n\t\t} catch (Throwable e) {\n\t\t\tLogs.redis().info(\"JedisPool ignore property user\");\n\t\t}\n\t\ttry {\n\n\t\t\treturn new JedisPool(conf, h.ip(), h.port(), conf.getConnectionTimeout(), conf.getTimeout(),\n\t\t\t\t\tconf.getPassword(), conf.getDb(), AppInfo.appId(\"sumk\"));\n\t\t} catch (Throwable e) {\n\t\t\tLogs.redis().info(\"JedisPool ignore property connectionTimeout\");\n\t\t}\n\t\treturn new JedisPool(conf, h.ip(), h.port(), conf.getTimeout(), conf.getPassword(), conf.getDb(),\n\t\t\t\tAppInfo.appId(\"sumk\"));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static JedisSentinelPool createSentinelPool(RedisConfig config) {\n\t\tList<Host> hosts = RedisSettings.parseHosts(config.hosts());\n\t\tSet<String> sentinels = new HashSet<>();\n\t\tfor (Host h : hosts) {\n\t\t\tsentinels.add(h.toString());\n\t\t}\n\t\tLogs.redis().info(\"create sentinel redis pool,sentinels={},db={}\", sentinels, config.getDb());\n\t\ttry {\n\t\t\treturn new JedisSentinelPool(config.getMaster(), sentinels, config, config.getConnectionTimeout(),\n\t\t\t\t\tconfig.getTimeout(), config.getUser(), config.getPassword(), config.getDb(), AppInfo.appId(\"sumk\"));\n\t\t} catch (Throwable e) {\n\t\t\tLogs.redis().info(\"JedisCluster ignore property user\");\n\t\t}\n\n\t\treturn new JedisSentinelPool(config.getMaster(), sentinels, config, config.getConnectionTimeout(),\n\t\t\t\tconfig.getTimeout(), config.getPassword(), config.getDb(), AppInfo.appId(\"sumk\"));\n\t}\n\n\tpublic static RedisCluster createJedisCluster(RedisConfig config) {\n\t\tList<Host> hosts = RedisSettings.parseHosts(config.hosts());\n\t\tSet<HostAndPort> hps = new HashSet<>();\n\t\tfor (Host h : hosts) {\n\t\t\thps.add(new HostAndPort(h.ip(), h.port()));\n\t\t}\n\t\tLogs.redis().info(\"create JedisCluster redis pool,hosts={}\", hps);\n\t\ttry {\n\t\t\treturn new RedisCluster(hps, config.getConnectionTimeout(), config.getTimeout(), config.getMaxAttempts(),\n\t\t\t\t\tconfig.getUser(), config.getPassword(), AppInfo.appId(\"sumk\"), config);\n\t\t} catch (Throwable e) {\n\t\t\tLogs.redis().info(\"JedisCluster ignore property user\");\n\t\t}\n\n\t\ttry {\n\t\t\treturn new RedisCluster(hps, config.getConnectionTimeout(), config.getTimeout(), config.getMaxAttempts(),\n\t\t\t\t\tconfig.getPassword(), AppInfo.appId(\"sumk\"), config);\n\t\t} catch (Throwable e) {\n\t\t\tLogs.redis().info(\"JedisCluster ignore property clientName\");\n\t\t}\n\n\t\treturn new RedisCluster(hps, config.getConnectionTimeout(), config.getTimeout(), config.getMaxAttempts(),\n\t\t\t\tconfig.getPassword(), config);\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/v3/Redis3x.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.v3;\n\nimport java.util.function.Function;\n\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.JedisPubSub;\n\n/**\n * jedis2.x 额外增加的方法\n */\npublic interface Redis3x {\n\t/**\n\t * 执行redis的批量操作或jedis的原生操作。<BR>\n\t * 注意:<B>如果是集群情况下，要保证所有的操作确实在key所对应的节点上才行，所以这个方法在集群环境里要慎重使用</B>\n\t * \n\t * @param <T>       返回值类型\n\t * @param sampleKey 只有在集群情况下才有意义，用于寻找真正的jedis节点\n\t * @param callback  批处理的代码\n\t * @return 返回的是callback的返回值\n\t */\n\t<T> T execute(String sampleKey, Function<Jedis, T> callback);\n\n\tLong publish(String channel, String message);\n\n\tvoid subscribe(JedisPubSub jedisPubSub, String... channels);\n\n\tvoid psubscribe(final JedisPubSub jedisPubSub, final String... patterns);\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/v3/RedisCluster.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.v3;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\nimport org.yx.redis.Redis;\nimport org.yx.redis.RedisConfig;\nimport org.yx.redis.RedisType;\n\nimport redis.clients.jedis.HostAndPort;\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.JedisCluster;\nimport redis.clients.jedis.JedisClusterCommand;\nimport redis.clients.jedis.JedisPool;\nimport redis.clients.jedis.params.SetParams;\n\npublic class RedisCluster extends JedisCluster implements Redis, Redis3x {\n\n\tprotected final String hosts;\n\tprotected final RedisConfig config;\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic RedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout, int maxAttempts,\n\t\t\tString user, String password, String clientName, RedisConfig redisConfig) {\n\t\tsuper(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, user, password, clientName, redisConfig);\n\t\tthis.config = redisConfig;\n\t\tthis.hosts = jedisClusterNode.toString();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic RedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout, int maxAttempts,\n\t\t\tString password, String clientName, RedisConfig redisConfig) {\n\t\tsuper(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, clientName, redisConfig);\n\t\tthis.config = redisConfig;\n\t\tthis.hosts = jedisClusterNode.toString();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\n\tpublic RedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout, int maxAttempts,\n\t\t\tString password, RedisConfig redisConfig) {\n\t\tsuper(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, redisConfig);\n\t\tthis.config = redisConfig;\n\t\tthis.hosts = jedisClusterNode.toString();\n\t}\n\n\t@Override\n\tpublic List<String> blpop(String... args) {\n\n\t\treturn super.blpop(AppInfo.getInt(\"sumk.redis.blpop.timeout\", 1000), args);\n\t}\n\n\t@Override\n\tpublic List<String> brpop(String... args) {\n\t\treturn super.brpop(AppInfo.getInt(\"sumk.redis.brpop.timeout\", 1000), args);\n\t}\n\n\t@Override\n\tpublic String watch(String... keys) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic String unwatch() {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic String randomKey() {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic String hosts() {\n\t\treturn this.hosts;\n\t}\n\n\t@Override\n\tpublic RedisConfig getRedisConfig() {\n\t\treturn this.config;\n\t}\n\n\t@Override\n\tpublic void shutdownPool() {\n\t\tsuper.close();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RedisCluster \" + hosts;\n\t}\n\n\t@Override\n\tpublic String scriptLoad(String script) {\n\t\tString ret = null;\n\t\tfor (Map.Entry<String, JedisPool> en : this.getClusterNodes().entrySet()) {\n\t\t\ttry (Jedis jedis = en.getValue().getResource()) {\n\t\t\t\tret = jedis.scriptLoad(script);\n\t\t\t} catch (RuntimeException e) {\n\t\t\t\tLogs.redis().error(\"分布式锁在{}初始化脚本失败,原因是：{}\", en.getKey(), e);\n\t\t\t\tthrow e;\n\t\t\t}\n\n\t\t}\n\t\treturn ret;\n\t}\n\n\t@Override\n\tpublic <T> T execute(String key, Function<Jedis, T> callback) {\n\t\treturn new JedisClusterCommand<T>(connectionHandler, maxAttempts) {\n\t\t\t@Override\n\t\t\tpublic T execute(Jedis connection) {\n\t\t\t\treturn callback.apply(connection);\n\t\t\t}\n\t\t}.run(key);\n\t}\n\n\t@Override\n\tpublic Redis mute() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean isMuted() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic RedisType redisType() {\n\t\treturn RedisType.CLUSTER;\n\t}\n\n\t@Override\n\tpublic String set(byte[] key, byte[] value, byte[] nxxx, byte[] expx, long time) {\n\t\tString NXXX = new String(nxxx, StandardCharsets.UTF_8);\n\t\tString EXPX = new String(expx, StandardCharsets.UTF_8);\n\t\tSetParams params = Redis3Kit.createSetParams(NXXX, EXPX, time);\n\t\treturn super.set(key, value, params);\n\t}\n\n\t@Override\n\tpublic String set(String key, String value, String nxxx, String expx, long time) {\n\t\tSetParams params = Redis3Kit.createSetParams(nxxx, expx, time);\n\t\treturn super.set(key, value, params);\n\t}\n\n\t@Override\n\tpublic String set(String key, String value, String expx, long time) {\n\t\tSetParams params = Redis3Kit.createSetParams(null, expx, time);\n\t\treturn super.set(key, value, params);\n\t}\n\n\t@Override\n\tpublic String set(String key, String value, String nxxx) {\n\t\tSetParams params = Redis3Kit.createSetParams(nxxx, null, -1);\n\t\treturn super.set(key, value, params);\n\t}\n\n\t@Override\n\tpublic String restore(String key, int ttl, byte[] serializedValue) {\n\t\treturn super.restore(key, (long) ttl, serializedValue);\n\t}\n\n\t@Override\n\tpublic Long expire(String key, int seconds) {\n\t\treturn super.expire(key, (long) seconds);\n\t}\n\n\t@Override\n\tpublic String setex(String key, int seconds, String value) {\n\t\treturn super.setex(key, (long) seconds, value);\n\t}\n\n\t@Override\n\tpublic Long move(String key, int dbIndex) {\n\t\treturn -1L;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/v3/RedisImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.v3;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport redis.clients.jedis.JedisPubSub;\n\npublic class RedisImpl extends AbstractRedis {\n\n\tpublic RedisImpl(JedisExecutor executor) {\n\t\tsuper(executor);\n\t}\n\n\t@Override\n\tpublic String get(String key) {\n\t\treturn execAndRetry(jedis -> jedis.get(key));\n\t}\n\n\t@Override\n\tpublic String type(String key) {\n\t\treturn execAndRetry(jedis -> jedis.type(key));\n\t}\n\n\t@Override\n\tpublic Long append(String key, String value) {\n\t\treturn execAndRetry(jedis -> jedis.append(key, value));\n\t}\n\n\t@Override\n\tpublic Set<String> keys(String pattern) {\n\t\treturn execAndRetry(jedis -> jedis.keys(pattern));\n\t}\n\n\t@Override\n\tpublic String set(String key, String value, String nxxx) {\n\t\treturn execAndRetry(jedis -> jedis.set(key, value, Redis3Kit.createSetParams(nxxx, null, -1)));\n\t}\n\n\t@Override\n\tpublic String set(String key, String value, String nxxx, String expx, long time) {\n\t\treturn execAndRetry(jedis -> jedis.set(key, value, Redis3Kit.createSetParams(nxxx, expx, time)));\n\t}\n\n\t@Override\n\tpublic String set(String key, String value, String expx, long time) {\n\t\treturn execAndRetry(jedis -> jedis.set(key, value, Redis3Kit.createSetParams(null, expx, time)));\n\t}\n\n\t@Override\n\tpublic String set(String key, String value) {\n\t\treturn execAndRetry(jedis -> jedis.set(key, value));\n\t}\n\n\t@Override\n\tpublic Boolean exists(String key) {\n\t\treturn execAndRetry(jedis -> jedis.exists(key));\n\t}\n\n\t@Override\n\tpublic Long exists(String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.exists(keys));\n\t}\n\n\t@Override\n\tpublic String rename(String oldkey, String newkey) {\n\t\treturn execAndRetry(jedis -> jedis.rename(oldkey, newkey));\n\t}\n\n\t@Override\n\tpublic Long sort(String key, String dstkey) {\n\t\treturn execAndRetry(jedis -> jedis.sort(key, dstkey));\n\t}\n\n\t@Override\n\tpublic List<String> sort(String key) {\n\t\treturn execAndRetry(jedis -> jedis.sort(key));\n\t}\n\n\t@Override\n\tpublic Long unlink(String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.unlink(keys));\n\t}\n\n\t@Override\n\tpublic Long unlink(String key) {\n\t\treturn execAndRetry(jedis -> jedis.unlink(key));\n\t}\n\n\t@Override\n\tpublic List<String> brpop(int timeout, String key) {\n\t\treturn execAndRetry(jedis -> jedis.brpop(timeout, key));\n\t}\n\n\t@Override\n\tpublic List<String> brpop(int timeout, String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.brpop(timeout, keys));\n\t}\n\n\t@Override\n\tpublic List<String> brpop(String... args) {\n\t\treturn execAndRetry(jedis -> jedis.brpop(args));\n\t}\n\n\t@Override\n\tpublic byte[] dump(String key) {\n\t\treturn execAndRetry(jedis -> jedis.dump(key));\n\t}\n\n\t@Override\n\tpublic Object eval(String script, List<String> keys, List<String> args) {\n\t\treturn execAndRetry(jedis -> jedis.eval(script, keys, args));\n\t}\n\n\t@Override\n\tpublic Object eval(String script, int keyCount, String... params) {\n\t\treturn execAndRetry(jedis -> jedis.eval(script, keyCount, params));\n\t}\n\n\t@Override\n\tpublic Object eval(String script, String sampleKey) {\n\t\treturn execAndRetry(jedis -> jedis.eval(script));\n\t}\n\n\t@Override\n\tpublic Long sadd(String key, String... members) {\n\t\treturn execAndRetry(jedis -> jedis.sadd(key, members));\n\t}\n\n\t@Override\n\tpublic Double hincrByFloat(String key, String field, double value) {\n\t\treturn execAndRetry(jedis -> jedis.hincrByFloat(key, field, value));\n\t}\n\n\t@Override\n\tpublic List<String> hvals(String key) {\n\t\treturn execAndRetry(jedis -> jedis.hvals(key));\n\t}\n\n\t@Override\n\tpublic List<String> hmget(String key, String... fields) {\n\t\treturn execAndRetry(jedis -> jedis.hmget(key, fields));\n\t}\n\n\t@Override\n\tpublic Map<String, String> hgetAll(String key) {\n\t\treturn execAndRetry(jedis -> jedis.hgetAll(key));\n\t}\n\n\t@Override\n\tpublic Long rpush(String key, String... strings) {\n\t\treturn execAndRetry(jedis -> jedis.rpush(key, strings));\n\t}\n\n\t@Override\n\tpublic Long llen(String key) {\n\t\treturn execAndRetry(jedis -> jedis.llen(key));\n\t}\n\n\t@Override\n\tpublic Long hlen(String key) {\n\t\treturn execAndRetry(jedis -> jedis.hlen(key));\n\t}\n\n\t@Override\n\tpublic String lindex(String key, long index) {\n\t\treturn execAndRetry(jedis -> jedis.lindex(key, index));\n\t}\n\n\t@Override\n\tpublic Long lpush(String key, String... strings) {\n\t\treturn execAndRetry(jedis -> jedis.lpush(key, strings));\n\t}\n\n\t@Override\n\tpublic Long lrem(String key, long count, String value) {\n\t\treturn execAndRetry(jedis -> jedis.lrem(key, count, value));\n\t}\n\n\t@Override\n\tpublic String lpop(String key) {\n\t\treturn execAndRetry(jedis -> jedis.lpop(key));\n\t}\n\n\t@Override\n\tpublic String lset(String key, long index, String value) {\n\t\treturn execAndRetry(jedis -> jedis.lset(key, index, value));\n\t}\n\n\t@Override\n\tpublic String rpop(String key) {\n\t\treturn execAndRetry(jedis -> jedis.rpop(key));\n\t}\n\n\t@Override\n\tpublic Long del(String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.del(keys));\n\t}\n\n\t@Override\n\tpublic Long del(String key) {\n\t\treturn execAndRetry(jedis -> jedis.del(key));\n\t}\n\n\t@Override\n\tpublic String restore(String key, int ttl, byte[] serializedValue) {\n\t\treturn execAndRetry(jedis -> jedis.restore(key, (long) ttl, serializedValue));\n\t}\n\n\t@Override\n\tpublic Long hdel(String key, String... fields) {\n\t\treturn execAndRetry(jedis -> jedis.hdel(key, fields));\n\t}\n\n\t@Override\n\tpublic String getSet(String key, String value) {\n\t\treturn execAndRetry(jedis -> jedis.getSet(key, value));\n\t}\n\n\t@Override\n\tpublic String ltrim(String key, long start, long stop) {\n\t\treturn execAndRetry(jedis -> jedis.ltrim(key, start, stop));\n\t}\n\n\t@Override\n\tpublic String setex(String key, int seconds, String value) {\n\t\treturn execAndRetry(jedis -> jedis.setex(key, (long) seconds, value));\n\t}\n\n\t@Override\n\tpublic List<String> lrange(String key, long start, long stop) {\n\t\treturn execAndRetry(jedis -> jedis.lrange(key, start, stop));\n\t}\n\n\t@Override\n\tpublic Long hsetnx(String key, String field, String value) {\n\t\treturn execAndRetry(jedis -> jedis.hsetnx(key, field, value));\n\t}\n\n\t@Override\n\tpublic String psetex(String key, long milliseconds, String value) {\n\t\treturn execAndRetry(jedis -> jedis.psetex(key, milliseconds, value));\n\t}\n\n\t@Override\n\tpublic Long persist(String key) {\n\t\treturn execAndRetry(jedis -> jedis.persist(key));\n\t}\n\n\t@Override\n\tpublic Set<String> hkeys(String key) {\n\t\treturn execAndRetry(jedis -> jedis.hkeys(key));\n\t}\n\n\t@Override\n\tpublic Long setnx(String key, String value) {\n\t\treturn execAndRetry(jedis -> jedis.setnx(key, value));\n\t}\n\n\t@Override\n\tpublic Long decrBy(String key, long decrement) {\n\t\treturn execAndRetry(jedis -> jedis.decrBy(key, decrement));\n\t}\n\n\t@Override\n\tpublic Long decr(String key) {\n\t\treturn execAndRetry(jedis -> jedis.decr(key));\n\t}\n\n\t@Override\n\tpublic Long hset(String key, String field, String value) {\n\t\treturn execAndRetry(jedis -> jedis.hset(key, field, value));\n\t}\n\n\t@Override\n\tpublic Long hset(String key, Map<String, String> hash) {\n\t\treturn execAndRetry(jedis -> jedis.hset(key, hash));\n\t}\n\n\t@Override\n\tpublic String hget(String key, String field) {\n\t\treturn execAndRetry(jedis -> jedis.hget(key, field));\n\t}\n\n\t@Override\n\tpublic String hmset(String key, Map<String, String> hash) {\n\t\treturn execAndRetry(jedis -> jedis.hmset(key, hash));\n\t}\n\n\t@Override\n\tpublic Boolean hexists(String key, String field) {\n\t\treturn execAndRetry(jedis -> jedis.hexists(key, field));\n\t}\n\n\t@Override\n\tpublic Long incrBy(String key, long increment) {\n\t\treturn execAndRetry(jedis -> jedis.incrBy(key, increment));\n\t}\n\n\t@Override\n\tpublic Double incrByFloat(String key, double increment) {\n\t\treturn execAndRetry(jedis -> jedis.incrByFloat(key, increment));\n\t}\n\n\t@Override\n\tpublic Long incr(String key) {\n\t\treturn execAndRetry(jedis -> jedis.incr(key));\n\t}\n\n\t@Override\n\tpublic Long expire(String key, int seconds) {\n\t\treturn execAndRetry(jedis -> jedis.expire(key, (long) seconds));\n\t}\n\n\t@Override\n\tpublic Long pexpire(String key, long milliseconds) {\n\t\treturn execAndRetry(jedis -> jedis.pexpire(key, milliseconds));\n\t}\n\n\t@Override\n\tpublic String substr(String key, int start, int end) {\n\t\treturn execAndRetry(jedis -> jedis.substr(key, start, end));\n\t}\n\n\t@Override\n\tpublic Long hincrBy(String key, String field, long value) {\n\t\treturn execAndRetry(jedis -> jedis.hincrBy(key, field, value));\n\t}\n\n\t@Override\n\tpublic Long ttl(String key) {\n\t\treturn execAndRetry(jedis -> jedis.ttl(key));\n\t}\n\n\t@Override\n\tpublic Long pttl(String key) {\n\t\treturn execAndRetry(jedis -> jedis.pttl(key));\n\t}\n\n\t@Override\n\tpublic Long touch(String key) {\n\t\treturn execAndRetry(jedis -> jedis.touch(key));\n\t}\n\n\t@Override\n\tpublic Long touch(String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.touch(keys));\n\t}\n\n\t@Override\n\tpublic Boolean setbit(String key, long offset, boolean value) {\n\t\treturn execAndRetry(jedis -> jedis.setbit(key, offset, value));\n\t}\n\n\t@Override\n\tpublic Long expireAt(String key, long unixTime) {\n\t\treturn execAndRetry(jedis -> jedis.expireAt(key, unixTime));\n\t}\n\n\t@Override\n\tpublic Long pexpireAt(String key, long millisecondsTimestamp) {\n\t\treturn execAndRetry(jedis -> jedis.pexpireAt(key, millisecondsTimestamp));\n\t}\n\n\t@Override\n\tpublic Boolean getbit(String key, long offset) {\n\t\treturn execAndRetry(jedis -> jedis.getbit(key, offset));\n\t}\n\n\t@Override\n\tpublic Long setrange(String key, long offset, String value) {\n\t\treturn execAndRetry(jedis -> jedis.setrange(key, offset, value));\n\t}\n\n\t@Override\n\tpublic String getrange(String key, long startOffset, long endOffset) {\n\t\treturn execAndRetry(jedis -> jedis.getrange(key, startOffset, endOffset));\n\t}\n\n\t@Override\n\tpublic Long msetnx(String... keysvalues) {\n\t\treturn execAndRetry(jedis -> jedis.msetnx(keysvalues));\n\t}\n\n\t@Override\n\tpublic Long zrevrank(String key, String member) {\n\t\treturn execAndRetry(jedis -> jedis.zrevrank(key, member));\n\t}\n\n\t@Override\n\tpublic Double zincrby(String key, double increment, String member) {\n\t\treturn execAndRetry(jedis -> jedis.zincrby(key, increment, member));\n\t}\n\n\t@Override\n\tpublic Long zrank(String key, String member) {\n\t\treturn execAndRetry(jedis -> jedis.zrank(key, member));\n\t}\n\n\t@Override\n\tpublic Long rpushx(String key, String... string) {\n\t\treturn execAndRetry(jedis -> jedis.rpushx(key, string));\n\t}\n\n\t@Override\n\tpublic List<String> srandmember(String key, int count) {\n\t\treturn execAndRetry(jedis -> jedis.srandmember(key, count));\n\t}\n\n\t@Override\n\tpublic String srandmember(String key) {\n\t\treturn execAndRetry(jedis -> jedis.srandmember(key));\n\t}\n\n\t@Override\n\tpublic Long pfadd(String key, String... elements) {\n\t\treturn execAndRetry(jedis -> jedis.pfadd(key, elements));\n\t}\n\n\t@Override\n\tpublic long pfcount(String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.pfcount(keys));\n\t}\n\n\t@Override\n\tpublic long pfcount(String key) {\n\t\treturn execAndRetry(jedis -> jedis.pfcount(key));\n\t}\n\n\t@Override\n\tpublic Double geodist(String key, String member1, String member2) {\n\t\treturn execAndRetry(jedis -> jedis.geodist(key, member1, member2));\n\t}\n\n\t@Override\n\tpublic Long zrem(String key, String... members) {\n\t\treturn execAndRetry(jedis -> jedis.zrem(key, members));\n\t}\n\n\t@Override\n\tpublic Set<String> zrangeByScore(String key, double min, double max) {\n\t\treturn execAndRetry(jedis -> jedis.zrangeByScore(key, min, max));\n\t}\n\n\t@Override\n\tpublic Set<String> zrangeByScore(String key, String min, String max) {\n\t\treturn execAndRetry(jedis -> jedis.zrangeByScore(key, min, max));\n\t}\n\n\t@Override\n\tpublic Set<String> zrangeByScore(String key, double min, double max, int offset, int count) {\n\t\treturn execAndRetry(jedis -> jedis.zrangeByScore(key, min, max, offset, count));\n\t}\n\n\t@Override\n\tpublic Set<String> zrangeByScore(String key, String min, String max, int offset, int count) {\n\t\treturn execAndRetry(jedis -> jedis.zrangeByScore(key, min, max, offset, count));\n\t}\n\n\t@Override\n\tpublic List<String> geohash(String key, String... members) {\n\t\treturn execAndRetry(jedis -> jedis.geohash(key, members));\n\t}\n\n\t@Override\n\tpublic List<String> mget(String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.mget(keys));\n\t}\n\n\t@Override\n\tpublic Long zcard(String key) {\n\t\treturn execAndRetry(jedis -> jedis.zcard(key));\n\t}\n\n\t@Override\n\tpublic String rpoplpush(String srckey, String dstkey) {\n\t\treturn execAndRetry(jedis -> jedis.rpoplpush(srckey, dstkey));\n\t}\n\n\t@Override\n\tpublic Long smove(String srckey, String dstkey, String member) {\n\t\treturn execAndRetry(jedis -> jedis.smove(srckey, dstkey, member));\n\t}\n\n\t@Override\n\tpublic Object evalsha(String sha1, int keyCount, String... params) {\n\t\treturn execAndRetry(jedis -> jedis.evalsha(sha1, keyCount, params));\n\t}\n\n\t@Override\n\tpublic Object evalsha(String sha1, String sampleKey) {\n\t\treturn execAndRetry(jedis -> jedis.evalsha(sha1));\n\t}\n\n\t@Override\n\tpublic Object evalsha(String sha1, List<String> keys, List<String> args) {\n\t\treturn execAndRetry(jedis -> jedis.evalsha(sha1, keys, args));\n\t}\n\n\t@Override\n\tpublic List<Long> bitfield(String key, String... arguments) {\n\t\treturn execAndRetry(jedis -> jedis.bitfield(key, arguments));\n\t}\n\n\t@Override\n\tpublic Long sunionstore(String dstkey, String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.sunionstore(dstkey, keys));\n\t}\n\n\t@Override\n\tpublic Long lpushx(String key, String... string) {\n\t\treturn execAndRetry(jedis -> jedis.lpushx(key, string));\n\t}\n\n\t@Override\n\tpublic String echo(String string) {\n\t\treturn execAndRetry(jedis -> jedis.echo(string));\n\t}\n\n\t@Override\n\tpublic Set<String> zrevrangeByScore(String key, double max, double min) {\n\t\treturn execAndRetry(jedis -> jedis.zrevrangeByScore(key, max, min));\n\t}\n\n\t@Override\n\tpublic Set<String> zrevrangeByScore(String key, double max, double min, int offset, int count) {\n\t\treturn execAndRetry(jedis -> jedis.zrevrangeByScore(key, max, min, offset, count));\n\t}\n\n\t@Override\n\tpublic Set<String> zrevrangeByScore(String key, String max, String min, int offset, int count) {\n\t\treturn execAndRetry(jedis -> jedis.zrevrangeByScore(key, max, min, offset, count));\n\t}\n\n\t@Override\n\tpublic Set<String> zrevrangeByScore(String key, String max, String min) {\n\t\treturn execAndRetry(jedis -> jedis.zrevrangeByScore(key, max, min));\n\t}\n\n\t@Override\n\tpublic Long srem(String key, String... members) {\n\t\treturn execAndRetry(jedis -> jedis.srem(key, members));\n\t}\n\n\t@Override\n\tpublic Long zcount(String key, String min, String max) {\n\t\treturn execAndRetry(jedis -> jedis.zcount(key, min, max));\n\t}\n\n\t@Override\n\tpublic Long zcount(String key, double min, double max) {\n\t\treturn execAndRetry(jedis -> jedis.zcount(key, min, max));\n\t}\n\n\t@Override\n\tpublic Set<String> zrangeByLex(String key, String min, String max, int offset, int count) {\n\t\treturn execAndRetry(jedis -> jedis.zrangeByLex(key, min, max, offset, count));\n\t}\n\n\t@Override\n\tpublic Set<String> zrangeByLex(String key, String min, String max) {\n\t\treturn execAndRetry(jedis -> jedis.zrangeByLex(key, min, max));\n\t}\n\n\t@Override\n\tpublic Set<String> sdiff(String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.sdiff(keys));\n\t}\n\n\t@Override\n\tpublic Set<String> sunion(String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.sunion(keys));\n\t}\n\n\t@Override\n\tpublic Long scard(String key) {\n\t\treturn execAndRetry(jedis -> jedis.scard(key));\n\t}\n\n\t@Override\n\tpublic Long renamenx(String oldkey, String newkey) {\n\t\treturn execAndRetry(jedis -> jedis.renamenx(oldkey, newkey));\n\t}\n\n\t@Override\n\tpublic Long zadd(String key, double score, String member) {\n\t\treturn execAndRetry(jedis -> jedis.zadd(key, score, member));\n\t}\n\n\t@Override\n\tpublic Long zadd(String key, Map<String, Double> scoreMembers) {\n\t\treturn execAndRetry(jedis -> jedis.zadd(key, scoreMembers));\n\t}\n\n\t@Override\n\tpublic Double zscore(String key, String member) {\n\t\treturn execAndRetry(jedis -> jedis.zscore(key, member));\n\t}\n\n\t@Override\n\tpublic Long zinterstore(String dstkey, String... sets) {\n\t\treturn execAndRetry(jedis -> jedis.zinterstore(dstkey, sets));\n\t}\n\n\t@Override\n\tpublic Long zunionstore(String dstkey, String... sets) {\n\t\treturn execAndRetry(jedis -> jedis.zunionstore(dstkey, sets));\n\t}\n\n\t@Override\n\tpublic Long sinterstore(String dstkey, String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.sinterstore(dstkey, keys));\n\t}\n\n\t@Override\n\tpublic String randomKey() {\n\t\treturn execAndRetry(jedis -> jedis.randomKey());\n\t}\n\n\t@Override\n\tpublic Long strlen(String key) {\n\t\treturn execAndRetry(jedis -> jedis.strlen(key));\n\t}\n\n\t@Override\n\tpublic Set<String> zrevrangeByLex(String key, String max, String min, int offset, int count) {\n\t\treturn execAndRetry(jedis -> jedis.zrevrangeByLex(key, max, min, offset, count));\n\t}\n\n\t@Override\n\tpublic Set<String> zrevrangeByLex(String key, String max, String min) {\n\t\treturn execAndRetry(jedis -> jedis.zrevrangeByLex(key, max, min));\n\t}\n\n\t@Override\n\tpublic String pfmerge(String destkey, String... sourcekeys) {\n\t\treturn execAndRetry(jedis -> jedis.pfmerge(destkey, sourcekeys));\n\t}\n\n\t@Override\n\tpublic Boolean sismember(String key, String member) {\n\t\treturn execAndRetry(jedis -> jedis.sismember(key, member));\n\t}\n\n\t@Override\n\tpublic Set<String> zrevrange(String key, long start, long stop) {\n\t\treturn execAndRetry(jedis -> jedis.zrevrange(key, start, stop));\n\t}\n\n\t@Override\n\tpublic String watch(String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.watch(keys));\n\t}\n\n\t@Override\n\tpublic Long publish(String channel, String message) {\n\t\treturn execAndRetry(jedis -> jedis.publish(channel, message));\n\t}\n\n\t@Override\n\tpublic Long bitcount(String key) {\n\t\treturn execAndRetry(jedis -> jedis.bitcount(key));\n\t}\n\n\t@Override\n\tpublic Long bitcount(String key, long start, long end) {\n\t\treturn execAndRetry(jedis -> jedis.bitcount(key, start, end));\n\t}\n\n\t@Override\n\tpublic Long zremrangeByLex(String key, String min, String max) {\n\t\treturn execAndRetry(jedis -> jedis.zremrangeByLex(key, min, max));\n\t}\n\n\t@Override\n\tpublic Long sdiffstore(String dstkey, String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.sdiffstore(dstkey, keys));\n\t}\n\n\t@Override\n\tpublic Long move(String key, int dbIndex) {\n\t\treturn execAndRetry(jedis -> jedis.move(key, dbIndex));\n\t}\n\n\t@Override\n\tpublic Long geoadd(String key, double longitude, double latitude, String member) {\n\t\treturn execAndRetry(jedis -> jedis.geoadd(key, longitude, latitude, member));\n\t}\n\n\t@Override\n\tpublic Set<String> smembers(String key) {\n\t\treturn execAndRetry(jedis -> jedis.smembers(key));\n\t}\n\n\t@Override\n\tpublic Set<String> zrange(String key, long start, long stop) {\n\t\treturn execAndRetry(jedis -> jedis.zrange(key, start, stop));\n\t}\n\n\t@Override\n\tpublic Long zlexcount(String key, String min, String max) {\n\t\treturn execAndRetry(jedis -> jedis.zlexcount(key, min, max));\n\t}\n\n\t@Override\n\tpublic Long hstrlen(String key, String field) {\n\t\treturn execAndRetry(jedis -> jedis.hstrlen(key, field));\n\t}\n\n\t@Override\n\tpublic Long zremrangeByScore(String key, double min, double max) {\n\t\treturn execAndRetry(jedis -> jedis.zremrangeByScore(key, min, max));\n\t}\n\n\t@Override\n\tpublic Long zremrangeByScore(String key, String min, String max) {\n\t\treturn execAndRetry(jedis -> jedis.zremrangeByScore(key, min, max));\n\t}\n\n\t@Override\n\tpublic String brpoplpush(String source, String destination, int timeout) {\n\t\treturn execAndRetry(jedis -> jedis.brpoplpush(source, destination, timeout));\n\t}\n\n\t@Override\n\tpublic Long zremrangeByRank(String key, long start, long stop) {\n\t\treturn execAndRetry(jedis -> jedis.zremrangeByRank(key, start, stop));\n\t}\n\n\t@Override\n\tpublic Set<String> sinter(String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.sinter(keys));\n\t}\n\n\t@Override\n\tpublic String scriptLoad(String script) {\n\t\treturn execAndRetry(jedis -> jedis.scriptLoad(script));\n\t}\n\n\t@Override\n\tpublic Set<String> spop(String key, long count) {\n\t\treturn execAndRetry(jedis -> jedis.spop(key, count));\n\t}\n\n\t@Override\n\tpublic String spop(String key) {\n\t\treturn execAndRetry(jedis -> jedis.spop(key));\n\t}\n\n\t@Override\n\tpublic List<String> blpop(int timeout, String... keys) {\n\t\treturn execAndRetry(jedis -> jedis.blpop(timeout, keys));\n\t}\n\n\t@Override\n\tpublic List<String> blpop(int timeout, String key) {\n\t\treturn execAndRetry(jedis -> jedis.blpop(timeout, key));\n\t}\n\n\t@Override\n\tpublic List<String> blpop(String... args) {\n\t\treturn execAndRetry(jedis -> jedis.blpop(args));\n\t}\n\n\t@Override\n\tpublic String mset(String... keysvalues) {\n\t\treturn execAndRetry(jedis -> jedis.mset(keysvalues));\n\t}\n\n\t@Override\n\tpublic byte[] get(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.get(key));\n\t}\n\n\t@Override\n\tpublic String type(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.type(key));\n\t}\n\n\t@Override\n\tpublic String set(byte[] key, byte[] value, byte[] nxxx, byte[] expx, long time) {\n\t\treturn execAndRetry(\n\t\t\t\tjedis -> jedis.set(key, value, Redis3Kit.createSetParams(new String(nxxx, StandardCharsets.UTF_8),\n\t\t\t\t\t\tnew String(expx, StandardCharsets.UTF_8), time)));\n\t}\n\n\t@Override\n\tpublic String set(byte[] key, byte[] value) {\n\t\treturn execAndRetry(jedis -> jedis.set(key, value));\n\t}\n\n\t@Override\n\tpublic Boolean exists(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.exists(key));\n\t}\n\n\t@Override\n\tpublic List<byte[]> hvals(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.hvals(key));\n\t}\n\n\t@Override\n\tpublic List<byte[]> hmget(byte[] key, byte[]... fields) {\n\t\treturn execAndRetry(jedis -> jedis.hmget(key, fields));\n\t}\n\n\t@Override\n\tpublic Map<byte[], byte[]> hgetAll(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.hgetAll(key));\n\t}\n\n\t@Override\n\tpublic Long rpush(byte[] key, byte[]... strings) {\n\t\treturn execAndRetry(jedis -> jedis.rpush(key, strings));\n\t}\n\n\t@Override\n\tpublic Long llen(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.llen(key));\n\t}\n\n\t@Override\n\tpublic byte[] lindex(byte[] key, long index) {\n\t\treturn execAndRetry(jedis -> jedis.lindex(key, index));\n\t}\n\n\t@Override\n\tpublic Long lpush(byte[] key, byte[]... strings) {\n\t\treturn execAndRetry(jedis -> jedis.lpush(key, strings));\n\t}\n\n\t@Override\n\tpublic Long lrem(byte[] key, long count, byte[] value) {\n\t\treturn execAndRetry(jedis -> jedis.lrem(key, count, value));\n\t}\n\n\t@Override\n\tpublic byte[] lpop(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.lpop(key));\n\t}\n\n\t@Override\n\tpublic String lset(byte[] key, long index, byte[] value) {\n\t\treturn execAndRetry(jedis -> jedis.lset(key, index, value));\n\t}\n\n\t@Override\n\tpublic byte[] rpop(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.rpop(key));\n\t}\n\n\t@Override\n\tpublic Long del(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.del(key));\n\t}\n\n\t@Override\n\tpublic Long hdel(byte[] key, byte[]... fields) {\n\t\treturn execAndRetry(jedis -> jedis.hdel(key, fields));\n\t}\n\n\t@Override\n\tpublic String ltrim(byte[] key, long start, long stop) {\n\t\treturn execAndRetry(jedis -> jedis.ltrim(key, start, stop));\n\t}\n\n\t@Override\n\tpublic List<byte[]> lrange(byte[] key, long start, long stop) {\n\t\treturn execAndRetry(jedis -> jedis.lrange(key, start, stop));\n\t}\n\n\t@Override\n\tpublic Long hsetnx(byte[] key, byte[] field, byte[] value) {\n\t\treturn execAndRetry(jedis -> jedis.hsetnx(key, field, value));\n\t}\n\n\t@Override\n\tpublic Set<byte[]> hkeys(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.hkeys(key));\n\t}\n\n\t@Override\n\tpublic Long hset(byte[] key, Map<byte[], byte[]> hash) {\n\t\treturn execAndRetry(jedis -> jedis.hset(key, hash));\n\t}\n\n\t@Override\n\tpublic Long hset(byte[] key, byte[] field, byte[] value) {\n\t\treturn execAndRetry(jedis -> jedis.hset(key, field, value));\n\t}\n\n\t@Override\n\tpublic byte[] hget(byte[] key, byte[] field) {\n\t\treturn execAndRetry(jedis -> jedis.hget(key, field));\n\t}\n\n\t@Override\n\tpublic String hmset(byte[] key, Map<byte[], byte[]> hash) {\n\t\treturn execAndRetry(jedis -> jedis.hmset(key, hash));\n\t}\n\n\t@Override\n\tpublic Boolean hexists(byte[] key, byte[] field) {\n\t\treturn execAndRetry(jedis -> jedis.hexists(key, field));\n\t}\n\n\t@Override\n\tpublic Long pexpire(byte[] key, long milliseconds) {\n\t\treturn execAndRetry(jedis -> jedis.pexpire(key, milliseconds));\n\t}\n\n\t@Override\n\tpublic Long pttl(byte[] key) {\n\t\treturn execAndRetry(jedis -> jedis.pttl(key));\n\t}\n\n\t@Override\n\tpublic Long setrange(byte[] key, long offset, byte[] value) {\n\t\treturn execAndRetry(jedis -> jedis.setrange(key, offset, value));\n\t}\n\n\t@Override\n\tpublic byte[] getrange(byte[] key, long startOffset, long endOffset) {\n\t\treturn execAndRetry(jedis -> jedis.getrange(key, startOffset, endOffset));\n\t}\n\n\t@Override\n\tpublic String unwatch() {\n\t\treturn execAndRetry(jedis -> jedis.unwatch());\n\t}\n\n\t@Override\n\tpublic void subscribe(JedisPubSub jedisPubSub, String... channels) {\n\t\texecAndRetry(jedis -> {\n\t\t\tjedis.subscribe(jedisPubSub, channels);\n\t\t\treturn \"OK\";\n\t\t});\n\t}\n\n\t@Override\n\tpublic void psubscribe(JedisPubSub jedisPubSub, String... patterns) {\n\t\texecAndRetry(jedis -> {\n\t\t\tjedis.psubscribe(jedisPubSub, patterns);\n\t\t\treturn \"OK\";\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/v3/SentinelJedisExecutor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.v3;\n\nimport org.yx.redis.RedisConfig;\nimport org.yx.redis.RedisType;\n\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.util.Pool;\n\npublic class SentinelJedisExecutor extends AbstractJedisExecutor {\n\n\tpublic SentinelJedisExecutor(RedisConfig config, Pool<Jedis> pool) {\n\t\tsuper(config, pool);\n\t}\n\n\t@Override\n\tpublic RedisType redisType() {\n\t\treturn RedisType.SENTINEL;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/java/org/yx/redis/v3/SingleJedisExecutor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.redis.v3;\n\nimport org.yx.log.Logs;\nimport org.yx.redis.Checkable;\nimport org.yx.redis.RedisConfig;\nimport org.yx.redis.RedisType;\n\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.util.Pool;\n\npublic class SingleJedisExecutor extends AbstractJedisExecutor implements Checkable {\n\n\tpublic SingleJedisExecutor(RedisConfig config, Pool<Jedis> pool) {\n\t\tsuper(config, pool);\n\t}\n\n\t@Override\n\tpublic boolean aliveCheck() {\n\t\tLogs.redis().trace(\"{}-{} begin alive check\", this, this.hashCode());\n\t\tfinal String PONE = \"PONG\";\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\ttry (Jedis jedis = jedis()) {\n\t\t\t\tString ret = jedis.ping();\n\n\t\t\t\tif (PONE.equalsIgnoreCase(ret)) {\n\t\t\t\t\tthis.disConnected = false;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tLogs.redis().warn(\"{} answer {}\", this, ret);\n\t\t\t} catch (Exception e) {\n\t\t\t\tif (!isConnectException(e)) {\n\t\t\t\t\tLogs.redis().error(e.getMessage(), e);\n\n\t\t\t\t\tif (!this.disConnected) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.disConnected = true;\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic RedisType redisType() {\n\t\treturn RedisType.SIMPLE;\n\t}\n}\n"
  },
  {
    "path": "sumk-framework/src/main/resources/META-INF/lua_del",
    "content": "if redis.call('get',KEYS[1]) == ARGV[1]\nthen\nreturn redis.call('del',KEYS[1])\nelse\nreturn 0\nend"
  },
  {
    "path": "sumk-framework/src/main/resources/META-INF/sumk.factories",
    "content": "sumk.ioc.bean=org.yx.common.listener.EventBusFactory,\\\n\torg.yx.common.validate.ComplexParamValidator,org.yx.common.validate.SimpleParamValidator,\\\n\torg.yx.redis.RedisPlugin,org.yx.redis.v3.Redis3Factory\nsumk.ioc.boot.watcher=org.yx.bean.BeanAssemblerBootWatcher\nsumk.ioc.optional=org.yx.redis.*\n\t"
  },
  {
    "path": "sumk-http/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright youtongluan (\\u6e38\\u901a\\u92ae\\uff0c\\u522b\\u540d\\uff1a\\u6e38\\u590f)\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "sumk-http/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.github.youtongluan</groupId>\n\t\t<artifactId>sumk</artifactId>\n\t\t<version>4.2.1</version>\n\t</parent>\n\t<artifactId>sumk-http</artifactId>\n\t<name>com.github.youtongluan:sumk</name>\n\t<description>A quick developing framewort for internet company</description>\n\t<url>https://github.com/youtongluan/sumk</url>\n\t<licenses>\n\t\t<license>\n\t\t\t<name>The Apache License, Version 2.0</name>\n\t\t\t<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n\t\t</license>\n\t</licenses>\n\t<scm>\n\t\t<url>https://github.com/youtongluan/sumk</url>\n\t\t<connection>https://github.com/youtongluan/sumk.git</connection>\n\t\t<developerConnection>https://github.com/youtongluan/sumk</developerConnection>\n\t</scm>\n\t<distributionManagement>\n\t    <repository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n\t    </repository>\n\t    <snapshotRepository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n\t    </snapshotRepository>\n\t</distributionManagement>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>sumk-framework</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t<artifactId>jetty-servlet</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/annotation/http/SumkFilter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.http;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport javax.servlet.DispatcherType;\n\n@Target({ ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface SumkFilter {\n\tString name() default \"\";\n\n\t/**\n\t * @return 要拦截的路径\n\t */\n\tString[] path();\n\n\tDispatcherType[] dispatcherType() default {};\n\n\t/**\n\t * @return true表示往后面添加\n\t */\n\tboolean isMatchAfter() default true;\n\n\tboolean asyncSupported() default true;\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/annotation/http/SumkServlet.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.http;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target({ ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface SumkServlet {\n\t/**\n\t * @return servlet的名字\n\t */\n\tString name() default \"\";\n\n\t/**\n\t * @return 访问路径\n\t */\n\tString[] path();\n\n\tint loadOnStartup() default -1;\n\n\tboolean asyncSupported() default false;\n\n\tString appKey() default \"\";\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/annotation/http/Upload.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.http;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 与@Web一起使用，表示该请求是multipart/form-data类型，一般而言就是文件上传。<BR>\n * 其中名字为paramName()的那个part，会解析成方法的参数\n */\n@Target({ ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Upload {\n\n\tString paramName() default \"param\";\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/annotation/http/Web.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.http;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.yx.http.MessageType;\n\n@Target({ ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Web {\n\n\t/**\n\t * @return 服务名称，如果为空，就根据方法名获取\n\t */\n\tString value() default \"\";\n\n\tString cnName() default \"\";\n\n\t/**\n\t * 本功能默认关闭。需要在启动的时候设置sumk.http.login.enable=1才能开启。\n\t * 开启本功能后，session中的userId会在整个调用链路上传递\n\t * \n\t * @return 如果已经开启了，并且该值为true，框架会校验当前用户是否存在session,以及是否过期\n\t */\n\tboolean requireLogin() default true;\n\n\tMessageType requestType() default MessageType.DEFAULT;\n\n\t/**\n\t * 为了调试方便，可以在启动的时候设置sumk.http.sign.enable=0来禁用它\n\t * \n\t * @return 如果设为true，框架会校验请求的签名\n\t */\n\tboolean sign() default false;\n\n\tMessageType responseType() default MessageType.DEFAULT;\n\n\t/**\n\t * 留给开发者做权限分组等，框架本身没有实际应用\n\t * \n\t * @return 标签（或分组）列表\n\t */\n\tString[] tags() default {};\n\n\tint toplimit() default 0;\n\n\tString[] method() default {};\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/HttpEncryptor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http;\n\nimport org.yx.http.handler.WebContext;\n\npublic interface HttpEncryptor {\n\tbyte[] encrypt(byte[] data, WebContext ctx) throws Exception;\n\n\tbyte[] decrypt(byte[] data, WebContext ctx) throws Exception;\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/HttpErrorCode.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http;\n\n/**\n * 3位数的错误码，为系统所保留，应用系统的错误码，要避开这个区间。客户端可以对这些异常码做额外处理，尤其是登录相关部分<BR>\n * 用户异常码推荐4位数，BizException中的code会对应http中json的异常码。<BR>\n * 这个错误码对应于http请求的返回码550时的异常码。它是body里的错误码（json格式）.<BR>\n * json有2个默认字段code、message，其中code为int类型<BR>\n */\npublic interface HttpErrorCode {\n\t/**\n\t * 线程数溢出\n\t */\n\tint THREAD_THRESHOLD_OVER = 900;\n\n\t/**\n\t * 登录失败\n\t */\n\tint LOGINFAILED = 901;\n\t/**\n\t * session过期或冲突\n\t */\n\tint SESSION_ERROR = 902;\n\n\t/**\n\t * 用户在其它地方登录了\n\t */\n\tint LOGIN_AGAIN = 903;\n\n\t/**\n\t * 熔断\n\t */\n\tint FUSING = 905;\n\n\t/**\n\t * 参数验证错误\n\t */\n\tint VALIDATE_ERROR = 910;\n\n\tint BODY_TOO_BIG = 911;\n\tint SIGN_EMPTY = 912;\n\tint SIGN_MISTAKE = 913;\n\tint FILE_MISS = 914;\n\n\t/**\n\t * sumk.http.upload.enable被设置为false，<code>@upload</code>注解被禁用\n\t */\n\tint UPLOAD_DISABLED = 930;\n\n\tint UPLOAD_NOT_MULTI_TYPE = 931;\n\n\tint UPLOAD_ANNOTATION_MISS = 932;\n\n\t/**\n\t * 加密用的key没有找到\n\t */\n\tint SESSION_KEY_NOT_FOUND = 940;\n\n\t/**\n\t * 请求处理出错\n\t */\n\tint HANDLE_ERROR = 950;\n\n\t/**\n\t * 请求格式不正确\n\t */\n\tint ACT_FORMAT_ERROR = 951;\n\n\t/**\n\t * 数据格式不正确\n\t */\n\tint DATA_FORMAT_ERROR = 952;\n\n\t/**\n\t * 框架处理的时候，出现了异常。这个异常可能是框架的，也可能是数据原因\n\t */\n\tint FRAMEWORK_ERROR = 953;\n\n\t/**\n\t * 接口没有定义，类似于404\n\t */\n\tint ACT_NOT_FOUND = 954;\n\n\t/**\n\t * 不支持该http方法，比如GET\n\t */\n\tint METHOD_UNSUPPORT = 960;\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/HttpHeaderName.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http;\n\nimport org.yx.conf.AppInfo;\n\npublic final class HttpHeaderName {\n\n\tprivate static String sessionId;\n\tprivate static String userFlag;\n\n\tpublic static void init() {\n\t\tsessionId = AppInfo.get(\"sumk.http.name.sessionId\", \"sid\");\n\t\tuserFlag = AppInfo.get(\"sumk.http.name.userFlag\", \"userFlag\");\n\t}\n\n\tpublic static String sessionId() {\n\t\treturn sessionId;\n\t}\n\n\tpublic static String userFlag() {\n\t\treturn userFlag;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/HttpJson.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http;\n\nimport java.util.Objects;\n\nimport org.yx.common.json.GsonHelper;\nimport org.yx.common.json.GsonOperator;\nimport org.yx.common.json.JsonOperator;\nimport org.yx.common.json.ServerJsonExclusionStrategy;\nimport org.yx.conf.AppInfo;\n\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.LongSerializationPolicy;\n\npublic final class HttpJson {\n\tprivate static JsonOperator operator = new GsonOperator(gsonBuilder().create());\n\n\tprivate static GsonBuilder gsonBuilder() {\n\t\tGsonBuilder gb = GsonHelper.builder(\"sumk.http\");\n\n\t\tif (AppInfo.getBoolean(\"sumk.http.json.long2String\", true)) {\n\t\t\tgb.setLongSerializationPolicy(LongSerializationPolicy.STRING);\n\t\t}\n\n\t\treturn ServerJsonExclusionStrategy.addServerExclusionStrategy(gb);\n\t}\n\n\tpublic static JsonOperator operator() {\n\t\treturn operator;\n\t}\n\n\tpublic static void setOperator(JsonOperator operator) {\n\t\tHttpJson.operator = Objects.requireNonNull(operator);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/HttpPlugin.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http;\n\nimport java.lang.reflect.Constructor;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.yx.annotation.Bean;\nimport org.yx.base.Lifecycle;\nimport org.yx.base.context.AppContext;\nimport org.yx.bean.IOC;\nimport org.yx.bean.InnerIOC;\nimport org.yx.bean.Plugin;\nimport org.yx.common.StringEntity;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.exception.SumkException;\nimport org.yx.http.act.HttpActionNode;\nimport org.yx.http.act.HttpActions;\nimport org.yx.http.handler.HttpHandler;\nimport org.yx.http.handler.HttpHandlerChain;\nimport org.yx.http.handler.RestType;\nimport org.yx.http.invoke.WebHandler;\nimport org.yx.http.kit.HttpSettings;\nimport org.yx.http.start.WebAnnotationResolver;\nimport org.yx.http.user.WebSessions;\nimport org.yx.log.Logs;\nimport org.yx.main.StartConstants;\nimport org.yx.main.SumkServer;\nimport org.yx.util.ExceptionUtil;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\n@Bean\npublic class HttpPlugin implements Plugin {\n\n\tprotected Lifecycle server;\n\n\t@Override\n\tpublic void stop() {\n\t\tif (this.server != null) {\n\t\t\tserver.stop();\n\t\t\tthis.server = null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn 10100;\n\t}\n\n\tprotected void resolveWebAnnotation(Collection<Object> beans) {\n\t\tWebAnnotationResolver factory = new WebAnnotationResolver();\n\t\ttry {\n\t\t\tList<StringEntity<HttpActionNode>> infos = new ArrayList<>(100);\n\t\t\tfor (Object bean : beans) {\n\t\t\t\tList<StringEntity<HttpActionNode>> acts = factory.resolve(bean);\n\t\t\t\tif (acts != null && acts.size() > 0) {\n\t\t\t\t\tinfos.addAll(acts);\n\t\t\t\t}\n\t\t\t}\n\t\t\tHttpActions.init(infos);\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void startAsync() {\n\t\tint port = SumkServer.httpPort();\n\t\tif (!this.isHttpEnable() || port < 1) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tHttpHeaderName.init();\n\t\t\tHttpSettings.init();\n\t\t\tresolveWebAnnotation(InnerIOC.beans());\n\t\t\tWebHandler.init();\n\t\t\tthis.buildHttpHandlers();\n\t\t\tthis.initServer(port);\n\t\t} catch (Exception e) {\n\t\t\tthrow ExceptionUtil.toRuntimeException(e);\n\t\t}\n\t}\n\n\tprotected void initServer(int port) throws Exception {\n\t\tString nojetty = StartConstants.EMBED_WEBSERVER_DISABLE;\n\t\tif (AppContext.inst().get(nojetty) != null || AppInfo.getBoolean(nojetty, false)) {\n\t\t\treturn;\n\t\t}\n\n\t\tString httpServerClass = StringUtil.isEmpty(AppInfo.get(Const.KEY_STORE_PATH)) ? \"JettyServer\"\n\t\t\t\t: \"JettyHttpsServer\";\n\t\tString hs = AppInfo.get(\"sumk.http.starter.class\", \"org.yx.http.start.\" + httpServerClass);\n\t\tClass<?> httpClz = Loader.loadClass(hs);\n\t\tConstructor<?> c = httpClz.getConstructor(int.class);\n\t\tthis.server = (Lifecycle) c.newInstance(port);\n\t}\n\n\tprotected void buildHttpHandlers() {\n\t\tList<HttpHandler> handlers = IOC.getBeans(HttpHandler.class);\n\t\tList<HttpHandler> restHandlers = new ArrayList<>(handlers.size());\n\t\tList<HttpHandler> uploadHandlers = new ArrayList<>(handlers.size());\n\t\tfor (HttpHandler h : handlers) {\n\t\t\tif (h.supportRestType(RestType.TEXT)) {\n\t\t\t\trestHandlers.add(h);\n\t\t\t}\n\t\t\tif (h.supportRestType(RestType.MULTI_PART)) {\n\t\t\t\tuploadHandlers.add(h);\n\t\t\t}\n\t\t}\n\t\tHttpHandlerChain.rest.setHandlers(restHandlers);\n\t\tLogger logger = Logs.http();\n\t\tif (logger.isDebugEnabled()) {\n\t\t\tlogger.debug(\"rest  handlers:{}\", this.buildString(restHandlers));\n\t\t}\n\t\tif (HttpSettings.isUploadEnable()) {\n\t\t\tHttpHandlerChain.multipart.setHandlers(uploadHandlers);\n\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\tlogger.debug(\"upload handlers:{}\", this.buildString(uploadHandlers));\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected boolean isHttpEnable() {\n\t\tif (!SumkServer.isHttpEnable()) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tLoader.loadClass(\"javax.servlet.http.HttpServlet\");\n\t\t} catch (Exception e) {\n\t\t\tLogs.http().warn(\"javax-servlet-api-**.jar is not imported\");\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void afterStarted() {\n\t\tLifecycle server = this.server;\n\t\tif (!SumkServer.isHttpEnable() || server == null) {\n\t\t\treturn;\n\t\t}\n\t\tWebSessions.initSession();\n\t\tserver.start();\n\t}\n\n\tprotected String buildString(List<HttpHandler> hs) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (HttpHandler h : hs) {\n\t\t\tsb.append(\" \").append(h.getClass().getSimpleName()).append(\"(\").append(h.order()).append(\")\");\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic Lifecycle getServer() {\n\t\treturn server;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/MessageType.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http;\n\npublic enum MessageType {\n\tDEFAULT(false, false), PLAIN(false, false), BASE64(false, true), ENCRYPT_BASE64(true, true), ENCRYPT(true, false);\n\n\tprivate final boolean encrypt;\n\tprivate final boolean base64;\n\n\tprivate MessageType(boolean encrypt, boolean base64) {\n\t\tthis.encrypt = encrypt;\n\t\tthis.base64 = base64;\n\t}\n\n\tpublic boolean isEncrypt() {\n\t\treturn this.encrypt;\n\t}\n\n\tpublic boolean isBase64() {\n\t\treturn this.base64;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/Signer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@FunctionalInterface\npublic interface Signer {\n\n\tString sign(byte[] bs, HttpServletRequest httpServletRequest) throws Exception;\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/WebFilter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http;\n\nimport java.util.Objects;\n\nimport org.yx.base.Ordered;\nimport org.yx.exception.SumkException;\nimport org.yx.http.handler.WebContext;\n\npublic abstract class WebFilter implements Ordered {\n\n\tprivate WebFilter next;\n\n\tpublic final void setNext(WebFilter next) {\n\t\tif (this.next != null) {\n\t\t\tthrow new SumkException(7682343, \"next已经赋值了，它是\" + this.next);\n\t\t}\n\t\tthis.next = Objects.requireNonNull(next);\n\t}\n\n\tprotected final Object callNextFilter(WebContext ctx) throws Throwable {\n\t\treturn this.next.doFilter(ctx);\n\t}\n\n\t/**\n\t * 本方法只需要实现，不需要去调用任何接口的本方法。<BR>\n\t * 这个方法里一般需要在里面调用callNextFilter(ctx)。\n\t * \n\t * @param ctx web请求的上下文\n\t * @return 返回给请求端的对象\n\t * @throws Throwable 如果抛出了异常，就会返回类似于“未知异常”这种提示信息\n\t */\n\tpublic abstract Object doFilter(WebContext ctx) throws Throwable;\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/WebUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http;\n\nimport java.nio.charset.Charset;\nimport java.util.List;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.http.act.HttpActions;\nimport org.yx.http.handler.MultipartHolder;\nimport org.yx.http.handler.MultipartItem;\nimport org.yx.http.handler.WebContext;\nimport org.yx.http.kit.HttpKit;\nimport org.yx.http.kit.InnerHttpUtil;\nimport org.yx.http.kit.LocalWebContext;\nimport org.yx.http.user.SessionObject;\nimport org.yx.http.user.WebSessions;\nimport org.yx.log.Logs;\n\n/**\n * <B>注意：本工具类不能在自定义的servlet中调用，比如login</B>\n */\npublic final class WebUtil {\n\n\tpublic static <T extends SessionObject> T getUserObject(Class<T> clz) {\n\t\treturn WebSessions.getUserObject(getSessionId(), clz);\n\t}\n\n\t/**\n\t * 移除session内容，也就是logout\n\t */\n\tpublic static void removeUserObject() {\n\t\tWebSessions.remove(getSessionId());\n\t}\n\n\tpublic static WebContext context() {\n\t\treturn LocalWebContext.getCtx();\n\t}\n\n\t/**\n\t * 获取http请求中的HttpServletRequest对象\n\t * \n\t * @return HttpServletRequest对象\n\t */\n\tpublic static HttpServletRequest getHttpRequest() {\n\t\tWebContext ctx = context();\n\t\tif (ctx != null) {\n\t\t\treturn ctx.httpRequest();\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * 从request对象获取sessionId\n\t * \n\t * @return sessionId\n\t */\n\tpublic static String getSessionId() {\n\t\treturn fromHeaderOrCookieOrParamter(getHttpRequest(), HttpHeaderName.sessionId());\n\t}\n\n\tprivate static String fromHeaderOrCookie(HttpServletRequest req, String name, boolean useCookie) {\n\t\tString value = req.getHeader(name);\n\t\tif (value != null && value.length() > 0) {\n\t\t\treturn value;\n\t\t}\n\t\tif (!useCookie) {\n\t\t\treturn null;\n\t\t}\n\t\tCookie[] cookies = req.getCookies();\n\t\tif (cookies == null || cookies.length == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (Cookie cookie : cookies) {\n\t\t\tif (name.equals(cookie.getName())) {\n\t\t\t\treturn cookie.getValue();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static String getValueFromRequest(HttpServletRequest req, @NotNull String name, boolean useCookie) {\n\t\tif (req == null) {\n\t\t\tLogs.http().info(\"request is null\");\n\t\t\treturn null;\n\t\t}\n\t\tObject attr = req.getAttribute(\"sumk.\".concat(name));\n\t\tif (attr != null && attr.getClass() == String.class) {\n\t\t\treturn (String) attr;\n\t\t}\n\t\tString v = fromHeaderOrCookie(req, name, useCookie);\n\t\tif (v != null) {\n\t\t\treturn v;\n\t\t}\n\t\treturn req.getParameter(name);\n\t}\n\n\tpublic static String fromHeaderOrCookieOrParamter(HttpServletRequest req, String name) {\n\t\treturn getValueFromRequest(req, name, true);\n\t}\n\n\tpublic static String fromHeaderOrParamter(HttpServletRequest req, String name) {\n\t\treturn getValueFromRequest(req, name, false);\n\t}\n\n\tpublic static String getUserFlag(HttpServletRequest req) {\n\t\treturn fromHeaderOrCookieOrParamter(req, HttpHeaderName.userFlag());\n\t}\n\n\tpublic static Charset getCharset(HttpServletRequest req) {\n\t\treturn InnerHttpUtil.charset(req);\n\t}\n\n\tpublic static String getUserId() {\n\t\tSessionObject obj = getUserObject(SessionObject.class);\n\t\tif (obj == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn obj.getUserId();\n\t}\n\n\t/**\n\t * 用这个方法获取上传项，<B>仅用于@upload修饰的接口</B>\n\t * \n\t * @return 除了参数外所有的multipart\n\t */\n\tpublic static List<MultipartItem> getMultiParts() {\n\t\treturn MultipartHolder.get();\n\t}\n\n\t/**\n\t * 根据name获取对应的MultipartItem，<B>仅用于@upload修饰的接口</B>\n\t * \n\t * @param name MultipartItem对应的名称，注意该名称是part的名称，而不是文件名\n\t * @return 对应的MultipartItem，如果不存在就返回null\n\t */\n\tpublic static MultipartItem getPart(String name) {\n\t\tList<MultipartItem> list = MultipartHolder.get();\n\t\tif (list == null || list.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (MultipartItem p : list) {\n\t\t\tif (name.equals(p.getName())) {\n\t\t\t\treturn p;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static HttpKit getKit() {\n\t\treturn InnerHttpUtil.getKit();\n\t}\n\n\tpublic static void setKit(HttpKit kit) {\n\t\tInnerHttpUtil.setKit(kit);\n\t}\n\n\tpublic static byte[] getSessionEncryptKey() {\n\t\tString sessionId = WebUtil.getSessionId();\n\t\tif (sessionId == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn WebSessions.loadUserSession().getEncryptKey(sessionId);\n\t}\n\n\t/**\n\t * @return 前缀匹配的方式里，获取剩余的url\n\t */\n\tpublic static String getUrlLeft() {\n\t\tWebContext ctx = context();\n\t\tString action = ctx.actionInfo().formalName();\n\t\tif (!action.endsWith(HttpActions.PREFIX_MATCH_ENDING)) {\n\t\t\treturn null;\n\t\t}\n\t\tString p = ctx.httpRequest().getPathInfo();\n\t\tif (p == null) {\n\t\t\treturn null;\n\t\t}\n\t\tp = HttpActions.formatActionName(p);\n\t\tif (p.length() < action.length()) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn p.substring(action.length() - 1);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/act/AbstractActionInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.act;\n\nimport java.util.Objects;\n\npublic abstract class AbstractActionInfo implements HttpActionInfo {\n\tprotected final String rawAct;\n\tprotected final HttpActionNode node;\n\t/**\n\t * 如果要支持url中的参数，可以考虑从这里入手\n\t */\n\tprotected final String formalName;\n\n\tpublic AbstractActionInfo(String rawAct, HttpActionNode node, String formatedName) {\n\t\tthis.rawAct = Objects.requireNonNull(rawAct);\n\t\tthis.node = Objects.requireNonNull(node);\n\t\tthis.formalName = Objects.requireNonNull(formatedName);\n\t}\n\n\tpublic String rawAct() {\n\t\treturn rawAct;\n\t}\n\n\tpublic HttpActionNode node() {\n\t\treturn node;\n\t}\n\n\t/**\n\t * @return 解析后正式的名字\n\t */\n\tpublic String formalName() {\n\t\treturn formalName;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/act/ActNameResolver.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.act;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport org.yx.util.StringUtil;\n\npublic class ActNameResolver implements Function<String, String> {\n\n\tprivate final boolean ignoreCase;\n\n\tpublic ActNameResolver(boolean ignoreCase) {\n\t\tthis.ignoreCase = ignoreCase;\n\t}\n\n\t@Override\n\tpublic String apply(String name) {\n\t\tString act = this.solve(Objects.requireNonNull(name));\n\t\tif (ignoreCase) {\n\t\t\treturn act.toLowerCase();\n\t\t}\n\t\treturn act;\n\t}\n\n\tprivate String solve(String name) {\n\t\tList<String> list = StringUtil.splitAndTrim(name, \"/\", \"\\\\\");\n\n\t\treturn String.join(\"/\", list);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/act/HttpActionInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.act;\n\npublic interface HttpActionInfo extends Comparable<HttpActionInfo> {\n\n\t/**\n\t * 原始路径\n\t * \n\t * @return <code>@Web</code>里定义的原始路径\n\t */\n\tString rawAct();\n\n\tHttpActionNode node();\n\n\t/**\n\t * @return 解析后正式的名字\n\t */\n\tString formalName();\n\n\t/**\n\t * 是否接受当前请求。有些实现类为了性能考虑，不对act做校验。\n\t * \n\t * @param act    格式化后的接口名,不为null\n\t * @param method 请求的http方法，不为null\n\t * @return true表示接受当前请求\n\t */\n\tboolean match(String act, String method);\n\n\tdefault int compareTo(HttpActionInfo o) {\n\t\treturn this.formalName().compareTo(o.formalName());\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/act/HttpActionNode.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.act;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.yx.bean.aop.asm.MethodPojo;\nimport org.yx.bean.aop.asm.ParamPojo;\nimport org.yx.bean.aop.context.CalleeNode;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.exception.BizException;\nimport org.yx.exception.SumkException;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.HttpJson;\nimport org.yx.http.MessageType;\nimport org.yx.http.kit.HttpSettings;\nimport org.yx.http.kit.InnerHttpUtil;\nimport org.yx.http.spec.HttpSpecs;\nimport org.yx.http.spec.UploadSpec;\nimport org.yx.http.spec.WebSpec;\nimport org.yx.log.Logs;\nimport org.yx.util.CollectionUtil;\n\npublic final class HttpActionNode extends CalleeNode {\n\n\tprivate final boolean requireLogin;\n\tprivate final MessageType requestType;\n\tprivate final boolean sign;\n\tprivate final MessageType responseType;\n\tprivate final List<String> httpMethod;\n\tprivate final List<String> tags;\n\tprivate final String cnName;\n\tprivate final UploadSpec upload;\n\n\tpublic UploadSpec upload() {\n\t\treturn this.upload;\n\t}\n\n\tpublic ParamPojo buildParamPojo(Object reqData) throws Exception {\n\t\tif (this.params.paramLength() == 0 || reqData == null) {\n\t\t\treturn createEmptyParamObj();\n\t\t}\n\t\tClass<? extends ParamPojo> argClz = this.params.paramClz();\n\t\tif (reqData.getClass() != String.class) {\n\t\t\tif (argClz.isInstance(reqData)) {\n\t\t\t\treturn argClz.cast(reqData);\n\t\t\t}\n\t\t\tthrow new SumkException(1245464, \"http argument \" + reqData.getClass().getName() + \" is not String\");\n\t\t}\n\t\tString data = (String) reqData;\n\t\tif (data.isEmpty()) {\n\t\t\treturn createEmptyParamObj();\n\t\t}\n\t\ttry {\n\t\t\treturn HttpJson.operator().fromJson(data, argClz);\n\t\t} catch (Exception e) {\n\t\t\tLogs.http().warn(\"json解析异常\", e);\n\t\t\tthrow BizException.create(HttpErrorCode.DATA_FORMAT_ERROR, \"数据格式错误\");\n\t\t}\n\t}\n\n\tprivate MessageType getMessageType(MessageType configed, String key) {\n\t\tif (configed != MessageType.DEFAULT) {\n\t\t\treturn configed;\n\t\t}\n\t\treturn InnerHttpUtil.parseMessageType(AppInfo.get(key));\n\t}\n\n\tprivate List<String> httpMethod(WebSpec web) {\n\t\tString[] m = web.method();\n\t\tif (m.length > 0) {\n\t\t\treturn CollectionUtil.unmodifyList(m);\n\t\t}\n\t\treturn HttpSettings.defaultHttpMethods();\n\t}\n\n\tpublic HttpActionNode(Object obj, Method method, MethodPojo argClzInfo, WebSpec action) {\n\t\tsuper(obj, method, argClzInfo, Objects.requireNonNull(action).toplimit() > 0 ? action.toplimit()\n\t\t\t\t: AppInfo.getInt(\"sumk.http.toplimit.default\", Const.DEFAULT_TOPLIMIT));\n\t\tthis.cnName = action.cnName();\n\t\tthis.httpMethod = httpMethod(action);\n\t\tthis.requestType = this.params.paramLength() == 0 ? MessageType.PLAIN\n\t\t\t\t: getMessageType(action.requestType(), \"sumk.http.request.type\");\n\t\tthis.responseType = getMessageType(action.responseType(), \"sumk.http.response.type\");\n\t\tthis.requireLogin = (action.requireLogin() && AppInfo.getBoolean(\"sumk.http.login.enable\", false))\n\t\t\t\t|| action.requestType().isEncrypt() || action.responseType().isEncrypt();\n\t\tthis.sign = this.params.paramLength() != 0 && action.sign()\n\t\t\t\t&& AppInfo.getBoolean(\"sumk.http.sign.enable\", true);\n\t\tthis.tags = CollectionUtil.unmodifyList(action.tags());\n\t\tthis.upload = HttpSpecs.extractUpload(obj, method);\n\t}\n\n\t/**\n\t * 返回值受@Web、sumk.http.login.enable配置、requestType、responseType影响。<BR>\n\t * 如果requestType、responseType中有一个为需要加密，它就返回true。\n\t * \n\t * @return true表示接口需要登录后才能访问\n\t */\n\tpublic boolean requireLogin() {\n\t\treturn requireLogin;\n\t}\n\n\t/**\n\t * 如果没有入参，那么它一定是PLAIN类型\n\t * \n\t * @return 请求体类型\n\t */\n\tpublic MessageType requestType() {\n\t\treturn requestType;\n\t}\n\n\t/**\n\t * 如果没有入参，那么它就一定是false\n\t * \n\t * @return 参数签名\n\t */\n\tpublic boolean sign() {\n\t\treturn sign;\n\t}\n\n\tpublic MessageType responseType() {\n\t\treturn responseType;\n\t}\n\n\tpublic boolean acceptMethod(String httpMethod) {\n\t\treturn this.httpMethod.contains(httpMethod);\n\t}\n\n\tpublic List<String> tags() {\n\t\treturn this.tags;\n\t}\n\n\tpublic List<String> methods() {\n\t\treturn this.httpMethod;\n\t}\n\n\tpublic String cnName() {\n\t\treturn cnName;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/act/HttpActions.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.act;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.base.matcher.Matchers;\nimport org.yx.common.StringEntity;\nimport org.yx.common.action.ActInfoUtil;\nimport org.yx.common.locale.I18n;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.exception.BizException;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.MessageType;\nimport org.yx.http.select.DefaultHttpActionSelector;\nimport org.yx.http.select.HttpActionSelector;\nimport org.yx.log.Logs;\nimport org.yx.main.SumkServer;\nimport org.yx.util.StringUtil;\n\npublic final class HttpActions {\n\n\tprivate static HttpActionSelector selector;\n\n\tprivate static Function<String, String> nameResolver;\n\tprivate static Predicate<String> fusing = BooleanMatcher.FALSE;\n\n\tpublic static final String PREFIX_MATCH_ENDING = \"/*\";\n\n\tpublic static synchronized void init(List<StringEntity<HttpActionNode>> infos) {\n\t\tif (nameResolver == null) {\n\t\t\tnameResolver = new ActNameResolver(AppInfo.getBoolean(\"sumk.http.act.ignorecase\", false));\n\t\t}\n\t\tif (selector == null) {\n\t\t\tselector = new DefaultHttpActionSelector();\n\t\t}\n\t\tif (infos == null || infos.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tselector.init(infos, nameResolver);\n\n\t\tAppInfo.addObserver(info -> {\n\t\t\tString fusings = AppInfo.getLatin(\"sumk.http.fusing\", null);\n\t\t\tif (fusings == null) {\n\t\t\t\tHttpActions.fusing = BooleanMatcher.FALSE;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tSet<String> set = new HashSet<>();\n\t\t\tfor (String f : StringUtil.splitAndTrim(fusings, Const.COMMA, Const.SEMICOLON)) {\n\t\t\t\tString act = formatActionName(f);\n\t\t\t\tif (act != null && act.length() > 0) {\n\t\t\t\t\tset.add(act);\n\t\t\t\t}\n\t\t\t}\n\t\t\tHttpActions.fusing = Matchers.createWildcardMatcher(set, 1);\n\t\t});\n\t}\n\n\tpublic static HttpActionInfo getHttpInfo(String requestAct, String method) {\n\t\tString usedAct = formatActionName(requestAct);\n\t\tif (usedAct == null || usedAct.isEmpty()) {\n\t\t\tLogs.http().error(\"act is empty for {}\", requestAct);\n\t\t\tthrow BizException.create(HttpErrorCode.ACT_FORMAT_ERROR,\n\t\t\t\t\tI18n.get(\"sumk.http.error.actformat\", \"{0}请求格式不正确\", requestAct));\n\t\t}\n\t\tif (fusing.test(usedAct)) {\n\t\t\tthrow BizException.create(HttpErrorCode.FUSING, I18n.get(\"sumk.http.error.fusing\", \"{0}请求被熔断\", usedAct));\n\t\t}\n\t\treturn selector.getHttpInfo(usedAct, method);\n\t}\n\n\tpublic static String formatActionName(String rawName) {\n\t\treturn nameResolver.apply(rawName);\n\t}\n\n\tpublic static Collection<HttpActionInfo> actions() {\n\t\treturn selector.actions();\n\t}\n\n\tpublic static List<Map<String, Object>> infos(boolean full) {\n\t\tif (!SumkServer.isHttpEnable()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<HttpActionInfo> infos = new ArrayList<>(actions());\n\t\tList<Map<String, Object>> ret = new ArrayList<>(infos.size());\n\t\tfor (HttpActionInfo info : infos) {\n\t\t\tHttpActionNode http = info.node();\n\t\t\tMap<String, Object> map = full ? ActInfoUtil.fullInfoMap(info.rawAct(), http)\n\t\t\t\t\t: ActInfoUtil.simpleInfoMap(info.rawAct(), http);\n\t\t\tret.add(map);\n\t\t\tmap.put(\"formalName\", info.formalName());\n\t\t\tmap.put(\"cnName\", http.cnName());\n\t\t\tmap.put(\"requireLogin\", http.requireLogin());\n\t\t\tif (http.requestType() != MessageType.PLAIN) {\n\t\t\t\tmap.put(\"requestEncrypt\", http.requestType());\n\t\t\t}\n\t\t\tif (http.responseType() != MessageType.PLAIN) {\n\t\t\t\tmap.put(\"responseEncrypt\", http.responseType());\n\t\t\t}\n\t\t\tif (http.sign()) {\n\t\t\t\tmap.put(\"sign\", http.sign());\n\t\t\t}\n\t\t\tif (http.toplimit() != Const.DEFAULT_TOPLIMIT) {\n\t\t\t\tmap.put(\"toplimit\", http.toplimit());\n\t\t\t}\n\t\t\tif (http.tags().size() > 0) {\n\t\t\t\tmap.put(\"tags\", http.tags());\n\t\t\t}\n\t\t\tmap.put(\"httpMethod\", http.methods());\n\n\t\t\tif (StringUtil.isNotEmpty(http.comment())) {\n\t\t\t\tmap.put(\"comment\", http.comment());\n\t\t\t}\n\t\t\tif (http.upload() != null) {\n\t\t\t\tmap.put(\"upload\", true);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\tpublic static HttpActionSelector getSelector() {\n\t\treturn selector;\n\t}\n\n\tpublic static void setSelector(HttpActionSelector selector) {\n\t\tHttpActions.selector = Objects.requireNonNull(selector);\n\t}\n\n\tpublic static void setNameResolver(Function<String, String> nameResolver) {\n\t\tHttpActions.nameResolver = Objects.requireNonNull(nameResolver);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/act/IgnoreNameActionInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.act;\n\n/**\n * 为了性能考虑，不对请求名做校验\n */\npublic class IgnoreNameActionInfo extends AbstractActionInfo {\n\n\tpublic IgnoreNameActionInfo(String rawAct, HttpActionNode node, String formatedName) {\n\t\tsuper(rawAct, node, formatedName);\n\t}\n\n\t@Override\n\tpublic boolean match(String act, String method) {\n\t\treturn node.acceptMethod(method);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/act/PrefixMappingActionInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.act;\n\npublic class PrefixMappingActionInfo extends AbstractActionInfo {\n\tprotected final String urlStart;\n\n\tpublic PrefixMappingActionInfo(String rawAct, HttpActionNode node, String formatedName, String urlStart) {\n\t\tsuper(rawAct, node, formatedName);\n\t\tthis.urlStart = urlStart;\n\t}\n\n\tpublic String getUrlStart() {\n\t\treturn urlStart;\n\t}\n\n\t@Override\n\tpublic boolean match(String act, String method) {\n\t\treturn act.startsWith(this.urlStart) && node.acceptMethod(method);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/AbstractHttpHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\npublic abstract class AbstractHttpHandler implements HttpHandler {\n\n\tprotected void setData(WebContext ctx, Object data) {\n\t\tctx.data(data);\n\t}\n\n\tprotected void setResult(WebContext ctx, Object result) {\n\t\tctx.result(result, true);\n\t}\n\n\tprotected void setResultNoCache(WebContext ctx, Object result) {\n\t\tctx.result(result, false);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/Base64DecodeHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\nimport org.yx.common.util.S;\nimport org.yx.http.kit.HttpSettings;\n\n@Bean\npublic class Base64DecodeHandler implements HttpHandler {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 1400;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Exception {\n\n\t\tif (!ctx.node().requestType().isBase64() || HttpSettings.allowPlain(ctx.httpRequest())) {\n\t\t\treturn;\n\t\t}\n\t\tbyte[] bs = ctx.getDataInByteArray();\n\t\tif (bs == null) {\n\t\t\treturn;\n\t\t}\n\t\tbyte[] data = S.base64().decode(bs);\n\t\tctx.data(data);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/Base64EncodeHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\nimport org.yx.common.util.S;\nimport org.yx.http.kit.HttpSettings;\n\n@Bean\npublic class Base64EncodeHandler implements HttpHandler {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 2400;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Exception {\n\t\tif (!ctx.node().responseType().isBase64() || HttpSettings.allowPlain(ctx.httpRequest())) {\n\t\t\treturn;\n\t\t}\n\t\tbyte[] bs = (byte[]) ctx.result();\n\t\tbyte[] data = S.base64().encode(bs);\n\t\tctx.result(data, false);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/DecryptHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\nimport org.yx.http.kit.HttpCiphers;\nimport org.yx.http.kit.HttpSettings;\n\n@Bean\npublic class DecryptHandler implements HttpHandler {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 1500;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Exception {\n\t\tif (!ctx.node().requestType().isEncrypt() || HttpSettings.allowPlain(ctx.httpRequest())) {\n\t\t\treturn;\n\t\t}\n\t\tbyte[] bs = ctx.getDataInByteArray();\n\t\tif (bs == null) {\n\t\t\treturn;\n\t\t}\n\t\tbyte[] data = HttpCiphers.getEncryptor().decrypt(bs, ctx);\n\t\tctx.data(data);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/EncryptHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\nimport org.yx.http.kit.HttpCiphers;\nimport org.yx.http.kit.HttpSettings;\n\n@Bean\npublic class EncryptHandler implements HttpHandler {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 2300;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Exception {\n\t\tif (!ctx.node().responseType().isEncrypt() || HttpSettings.allowPlain(ctx.httpRequest())) {\n\t\t\treturn;\n\t\t}\n\t\tbyte[] bs = (byte[]) ctx.result();\n\t\tbyte[] data = HttpCiphers.getEncryptor().encrypt(bs, ctx);\n\t\tctx.result(data, false);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/HttpHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.base.Ordered;\n\npublic interface HttpHandler extends Ordered {\n\n\tvoid handle(WebContext ctx) throws Throwable;\n\n\tdefault boolean supportRestType(String type) {\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/HttpHandlerChain.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.yx.log.Log;\n\npublic final class HttpHandlerChain {\n\n\tpublic static final HttpHandlerChain rest = new HttpHandlerChain();\n\tpublic static final HttpHandlerChain multipart = new HttpHandlerChain();\n\n\tprivate Logger LOG = Log.get(\"sumk.http.chain\");\n\tprivate HttpHandler[] handlers;\n\n\tpublic void setHandlers(List<HttpHandler> handlers) {\n\t\tthis.handlers = handlers.toArray(new HttpHandler[handlers.size()]);\n\t}\n\n\tpublic void handle(WebContext ctx) throws Throwable {\n\t\tfor (HttpHandler h : this.handlers) {\n\t\t\tif (h.order() < ctx.getLowestOrder()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (LOG.isTraceEnabled()) {\n\t\t\t\tif (ctx.data() instanceof String) {\n\t\t\t\t\tString s = ((String) ctx.data());\n\t\t\t\t\tLOG.trace(\"{} - {} with data:{}\", ctx.rawAct(), h.getClass().getSimpleName(), s);\n\t\t\t\t}\n\t\t\t}\n\t\t\th.handle(ctx);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/InvokeHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\nimport org.yx.http.invoke.WebHandler;\n\n@Bean\npublic class InvokeHandler implements HttpHandler {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 2000;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Throwable {\n\t\tctx.result(WebHandler.handle(ctx), true);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/MultiItemImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Collection;\nimport java.util.Objects;\n\nimport javax.servlet.http.Part;\n\npublic class MultiItemImpl implements MultipartItem {\n\tprivate final Part part;\n\n\tpublic MultiItemImpl(Part part) {\n\t\tthis.part = Objects.requireNonNull(part);\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn part.getName();\n\t}\n\n\t@Override\n\tpublic long getSize() {\n\t\treturn part.getSize();\n\t}\n\n\t@Override\n\tpublic InputStream getInputStream() throws IOException {\n\t\treturn part.getInputStream();\n\t}\n\n\t@Override\n\tpublic String getSubmittedFileName() {\n\t\treturn part.getSubmittedFileName();\n\t}\n\n\t@Override\n\tpublic String getHeader(String name) {\n\t\treturn part.getHeader(name);\n\t}\n\n\t@Override\n\tpublic Collection<String> getHeaders(String name) {\n\t\treturn part.getHeaders(name);\n\t}\n\n\t@Override\n\tpublic Collection<String> getHeaderNames() {\n\t\treturn part.getHeaderNames();\n\t}\n\n\t@Override\n\tpublic String getContentType() {\n\t\treturn part.getContentType();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/MultipartHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.servlet.http.Part;\n\nimport org.yx.annotation.Bean;\nimport org.yx.common.locale.I18n;\nimport org.yx.exception.BizException;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.kit.InnerHttpUtil;\nimport org.yx.http.spec.UploadSpec;\nimport org.yx.log.Logs;\n\n@Bean\npublic class MultipartHandler implements HttpHandler {\n\n\t@Override\n\tpublic boolean supportRestType(String type) {\n\t\treturn RestType.MULTI_PART.equals(type);\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn 1300;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Throwable {\n\t\tUploadSpec upload = ctx.node().upload();\n\t\tif (upload == null) {\n\t\t\tLogs.http().error(\"{}缺少 @Upload\", ctx.rawAct());\n\t\t\tthrow BizException.create(HttpErrorCode.UPLOAD_ANNOTATION_MISS,\n\t\t\t\t\tI18n.get(\"sumk.http.upload.error.annocation\", \"不是上传接口:{0}\", ctx.rawAct()));\n\t\t}\n\t\tCollection<Part> list = ctx.httpRequest().getParts();\n\t\tif (list == null || list.isEmpty()) {\n\t\t\tthrow BizException.create(HttpErrorCode.FILE_MISS, \"没有文件\");\n\t\t}\n\t\tList<MultipartItem> files = new ArrayList<>(list.size());\n\t\tfor (Part p : list) {\n\n\t\t\tif (upload.paramName().equals(p.getName())) {\n\t\t\t\tctx.data(InnerHttpUtil.extractData(p.getInputStream(), (int) p.getSize()));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfiles.add(new MultiItemImpl(p));\n\t\t}\n\t\tMultipartHolder.set(Collections.unmodifiableList(files));\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/MultipartHolder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport java.util.List;\n\npublic final class MultipartHolder {\n\tprivate static final ThreadLocal<List<MultipartItem>> items = new ThreadLocal<>();\n\n\tpublic static List<MultipartItem> get() {\n\t\treturn items.get();\n\t}\n\n\tstatic void set(List<MultipartItem> fs) {\n\t\titems.set(fs);\n\t}\n\n\tpublic static void remove() {\n\t\titems.remove();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/MultipartItem.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Collection;\n\npublic interface MultipartItem {\n\n\tString getName();\n\n\tlong getSize();\n\n\tInputStream getInputStream() throws IOException;\n\n\tString getSubmittedFileName();\n\n\tString getHeader(String name);\n\n\tCollection<String> getHeaders(String name);\n\n\tCollection<String> getHeaderNames();\n\n\tString getContentType();\n\n}"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/ReqDataHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.yx.annotation.Bean;\nimport org.yx.http.kit.InnerHttpUtil;\nimport org.yx.log.Logs;\n\n@Bean\npublic class ReqDataHandler implements HttpHandler {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 1300;\n\t}\n\n\t@Override\n\tpublic boolean supportRestType(String type) {\n\t\treturn RestType.TEXT.equals(type);\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Exception {\n\t\tif (ctx.data() != null) {\n\t\t\tLogs.http().debug(\"data is not null\");\n\t\t\treturn;\n\t\t}\n\t\tif (ctx.node().paramLength() == 0) {\n\t\t\treturn;\n\t\t}\n\t\tHttpServletRequest req = ctx.httpRequest();\n\t\tString data = req.getParameter(\"data\");\n\t\tif (data != null) {\n\t\t\tctx.data(data);\n\t\t\treturn;\n\t\t}\n\t\tctx.data(InnerHttpUtil.extractData(req.getInputStream(), req.getContentLength()));\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/ReqSignValidateHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.BizException;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.WebUtil;\nimport org.yx.http.kit.HttpCiphers;\nimport org.yx.log.Logs;\nimport org.yx.util.StringUtil;\n\n@Bean\npublic class ReqSignValidateHandler implements HttpHandler {\n\n\tprivate byte[] salt;\n\n\t@Override\n\tpublic int order() {\n\t\treturn 1600;\n\t}\n\n\tpublic ReqSignValidateHandler() {\n\n\t\tString saltStr = AppInfo.get(\"sumk.http.sign.salt\");\n\t\tif (StringUtil.isNotEmpty(saltStr)) {\n\t\t\tsalt = saltStr.getBytes();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Exception {\n\t\tif (!ctx.node().sign()) {\n\t\t\treturn;\n\t\t}\n\t\tbyte[] bs = ctx.getDataInByteArray();\n\t\tif (bs == null) {\n\t\t\treturn;\n\t\t}\n\t\tString sign = WebUtil.fromHeaderOrParamter(ctx.httpRequest(), \"__sign\");\n\t\tif (StringUtil.isEmpty(sign)) {\n\t\t\tthrow BizException.create(HttpErrorCode.SIGN_EMPTY, \"签名不能为空\");\n\t\t}\n\t\tif (salt != null) {\n\t\t\tbyte[] temp = new byte[bs.length + salt.length];\n\t\t\tSystem.arraycopy(bs, 0, temp, 0, bs.length);\n\t\t\tSystem.arraycopy(salt, 0, temp, bs.length, salt.length);\n\t\t\tbs = temp;\n\t\t}\n\t\tString sign1 = HttpCiphers.getSigner().sign(bs, ctx.httpRequest());\n\t\tif (!sign.equals(sign1)) {\n\t\t\tLogs.http().debug(\"client sign:{},computed is:{}\", sign, sign1);\n\t\t\tthrow BizException.create(HttpErrorCode.SIGN_MISTAKE, \"签名验证错误\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/ReqToStringHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\n\n@Bean\npublic class ReqToStringHandler implements HttpHandler {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 1700;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Exception {\n\t\tObject obj = ctx.data();\n\t\tif (obj == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (!(obj instanceof byte[])) {\n\t\t\treturn;\n\t\t}\n\t\tbyte[] bs = (byte[]) obj;\n\t\tString data = new String(bs, ctx.charset());\n\t\tctx.data(data);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/ReqUserHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\nimport org.yx.base.context.ActionContext;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.BizException;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.WebUtil;\nimport org.yx.http.kit.HttpSettings;\nimport org.yx.http.user.SessionObject;\nimport org.yx.http.user.UserSession;\nimport org.yx.http.user.WebSessions;\nimport org.yx.log.Logs;\nimport org.yx.util.StringUtil;\n\n@Bean\npublic class ReqUserHandler implements HttpHandler {\n\n\tprivate boolean tokenMode = AppInfo.getBoolean(\"sumk.http.session.token\", false);\n\n\t@Override\n\tpublic int order() {\n\t\treturn 1200;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Exception {\n\t\tif (ctx.node().requireLogin()) {\n\t\t\tcheckSession(WebUtil.getSessionId(), WebUtil.getUserFlag(ctx.httpRequest()));\n\t\t}\n\t}\n\n\tpublic void checkSession(String sessionId, String userId) {\n\t\tif (!WebSessions.getSessionIdVerifier().test(sessionId)) {\n\t\t\tLogs.http().warn(\"sessionId:{}, is not valid\", sessionId);\n\t\t\tthrow BizException.create(HttpErrorCode.SESSION_ERROR, \"token无效\");\n\t\t}\n\t\tUserSession session = WebSessions.loadUserSession();\n\n\t\tSessionObject obj = tokenMode ? session.getUserObject(sessionId, SessionObject.class)\n\t\t\t\t: session.loadAndRefresh(sessionId, SessionObject.class);\n\n\t\tif (obj == null) {\n\n\t\t\tif (HttpSettings.isSingleLogin() && StringUtil.isNotEmpty(userId) && session.sessionId(userId) != null) {\n\t\t\t\tLogs.http().info(\"sessionId:{}, login by other place\", sessionId);\n\t\t\t\tthrow BizException.create(HttpErrorCode.LOGIN_AGAIN, \"您已在其他地方登录！\");\n\t\t\t}\n\t\t\tLogs.http().info(\"sessionId:{}, 没找到对应的session\", sessionId);\n\t\t\tthrow BizException.create(HttpErrorCode.SESSION_ERROR, \"请重新登录\");\n\t\t}\n\t\tActionContext.current().userId(obj.getUserId());\n\n\t\tLong deadTime = obj.getExpiredTime();\n\t\tif (deadTime != null) {\n\t\t\tif (deadTime < System.currentTimeMillis()) {\n\t\t\t\tLogs.http().warn(\"sessionId:{}, expiredTime:{}，使用时间太长\", sessionId, deadTime);\n\t\t\t\tthrow BizException.create(HttpErrorCode.SESSION_ERROR, \"session使用时间太长\");\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/RespBodyHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\n\n@Bean\npublic class RespBodyHandler implements HttpHandler {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 2600;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Throwable {\n\t\tbyte[] data = (byte[]) ctx.result();\n\t\tif (data != null && data.length > 0) {\n\t\t\tctx.httpResponse().getOutputStream().write(data);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/RespToStringHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\nimport org.yx.http.HttpJson;\n\n@Bean\npublic class RespToStringHandler implements HttpHandler {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 2100;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Throwable {\n\t\tObject obj = ctx.result();\n\t\tif (obj == null) {\n\t\t\tif (ctx.node().getReturnType() == void.class) {\n\t\t\t\tctx.result(new byte[0], false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tctx.result(HttpJson.operator().toJson(obj), true);\n\t\t\treturn;\n\t\t}\n\t\tClass<?> clz = obj.getClass();\n\t\tif (clz == byte[].class) {\n\t\t\treturn;\n\t\t}\n\t\tif (clz.isPrimitive() || clz.equals(String.class)) {\n\t\t\tctx.result(String.valueOf(obj), true);\n\t\t\treturn;\n\t\t}\n\t\tctx.result(HttpJson.operator().toJson(obj), true);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/RestType.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\npublic final class RestType {\n\t/**\n\t * 普通的http请求\n\t */\n\tpublic static final String TEXT = \"text\";\n\n\t/**\n\t * 文件上传\n\t */\n\tpublic static final String MULTI_PART = \"multipart\";\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/ToBytesHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport org.yx.annotation.Bean;\n\n@Bean\npublic class ToBytesHandler implements HttpHandler {\n\n\t@Override\n\tpublic int order() {\n\t\treturn 2200;\n\t}\n\n\t@Override\n\tpublic void handle(WebContext ctx) throws Exception {\n\t\tObject result = ctx.result();\n\t\tif (result == null || result.getClass() == byte[].class) {\n\t\t\treturn;\n\t\t}\n\t\tString bs = (String) result;\n\t\tctx.result(bs.getBytes(ctx.charset()), false);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/handler/WebContext.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.handler;\n\nimport java.nio.charset.Charset;\nimport java.util.List;\nimport java.util.Objects;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.yx.bean.aop.context.NodeContext;\nimport org.yx.http.act.HttpActionInfo;\nimport org.yx.http.act.HttpActionNode;\n\npublic class WebContext extends NodeContext<HttpActionNode> {\n\n\tprivate final HttpActionInfo actionInfo;\n\tprivate final HttpServletRequest httpRequest;\n\tprivate final HttpServletResponse httpResponse;\n\tprivate final Charset charset;\n\tprivate Object data;\n\n\tprivate int lowestOrder;\n\n\tprivate Object result;\n\tprivate transient String str_data;\n\tprivate transient String str_resp;\n\n\tprivate boolean failed;\n\n\tpublic String dataInString() {\n\t\treturn str_data;\n\t}\n\n\tpublic String respInString() {\n\t\treturn str_resp;\n\t}\n\n\tpublic Object result() {\n\t\treturn result;\n\t}\n\n\tvoid result(Object result, boolean cacheInStr) {\n\t\tthis.result = result;\n\t\tif (cacheInStr && result != null && String.class == result.getClass()) {\n\t\t\tthis.str_resp = (String) result;\n\t\t}\n\t}\n\n\tpublic WebContext(HttpActionInfo info, HttpServletRequest req, HttpServletResponse resp, Charset charset) {\n\t\tthis.actionInfo = info;\n\t\tthis.httpRequest = Objects.requireNonNull(req);\n\t\tthis.charset = Objects.requireNonNull(charset);\n\t\tthis.httpResponse = resp;\n\t}\n\n\tpublic Charset charset() {\n\t\treturn this.charset;\n\t}\n\n\tpublic Object data() {\n\t\treturn data;\n\t}\n\n\tpublic byte[] getDataInByteArray() {\n\t\tif (data instanceof String) {\n\t\t\treturn ((String) data).getBytes(charset());\n\t\t}\n\t\treturn (byte[]) data;\n\t}\n\n\tpublic HttpServletRequest httpRequest() {\n\t\treturn httpRequest;\n\t}\n\n\tpublic HttpServletResponse httpResponse() {\n\t\treturn httpResponse;\n\t}\n\n\tvoid data(Object data) {\n\t\tthis.data = data;\n\t\tif (data != null && String.class == data.getClass()) {\n\t\t\tthis.str_data = (String) data;\n\t\t}\n\t}\n\n\t/**\n\t * 开发者定义的原始act，这里的act不包含逗号。 如果有逗号分隔，这里就只是@Web中的一段\n\t * \n\t * @return 就是@Web上定义的act，不为null\n\t */\n\tpublic String rawAct() {\n\t\treturn this.actionInfo.rawAct();\n\t}\n\n\tpublic int getLowestOrder() {\n\t\treturn lowestOrder;\n\t}\n\n\tpublic void setLowestOrder(int lowestOrder) {\n\t\tthis.lowestOrder = lowestOrder;\n\t}\n\n\tpublic List<String> tags() {\n\t\treturn this.node().tags();\n\t}\n\n\tpublic boolean isFailed() {\n\t\treturn failed;\n\t}\n\n\tpublic void setFailed(boolean failed) {\n\t\tthis.failed = failed;\n\t}\n\n\tpublic HttpActionInfo actionInfo() {\n\t\treturn actionInfo;\n\t}\n\n\t@Override\n\tpublic HttpActionNode node() {\n\t\treturn this.actionInfo.node();\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/invoke/WebHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.invoke;\n\nimport java.util.List;\n\nimport org.yx.bean.IOC;\nimport org.yx.http.WebFilter;\nimport org.yx.http.handler.WebContext;\n\npublic final class WebHandler {\n\n\tprivate static WebFilter filter;\n\tprivate static final WebFilter LAST = new WebFilter() {\n\n\t\tprivate final WebVisitor visitor = new WebVisitorImpl();\n\n\t\t@Override\n\t\tpublic Object doFilter(WebContext ctx) throws Throwable {\n\t\t\treturn visitor.visit(ctx);\n\t\t}\n\n\t};\n\n\tpublic static synchronized void init() {\n\t\tif (filter != null) {\n\t\t\treturn;\n\t\t}\n\t\tList<WebFilter> list = IOC.getBeans(WebFilter.class);\n\t\tif (list == null || list.isEmpty()) {\n\t\t\tfilter = LAST;\n\t\t\treturn;\n\t\t}\n\t\tfinal int size = list.size();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tWebFilter current = list.get(i);\n\t\t\tif (i == size - 1) {\n\t\t\t\tcurrent.setNext(LAST);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcurrent.setNext(list.get(i + 1));\n\t\t}\n\t\tfilter = list.get(0);\n\t}\n\n\tpublic static Object handle(WebContext ctx) throws Throwable {\n\t\tctx.setParamPojo(ctx.node().buildParamPojo(ctx.data()));\n\t\treturn filter.doFilter(ctx);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/invoke/WebVisitor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.invoke;\n\nimport org.yx.http.handler.WebContext;\n\npublic interface WebVisitor {\n\n\tObject visit(WebContext ctx) throws Throwable;\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/invoke/WebVisitorImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.invoke;\n\nimport org.yx.http.act.HttpActionNode;\nimport org.yx.http.handler.WebContext;\n\npublic class WebVisitorImpl implements WebVisitor {\n\n\t@Override\n\tpublic Object visit(WebContext ctx) throws Throwable {\n\t\tHttpActionNode http = ctx.node();\n\t\treturn http.execute(ctx.getParamPojo());\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/kit/DefaultHttpEncryptor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.kit;\n\nimport java.util.Objects;\n\nimport org.yx.common.util.S;\nimport org.yx.common.util.secury.Encryptor;\nimport org.yx.exception.BizException;\nimport org.yx.http.HttpEncryptor;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.WebUtil;\nimport org.yx.http.handler.WebContext;\n\npublic class DefaultHttpEncryptor implements HttpEncryptor {\n\n\tprivate Encryptor cipher = S.cipher();\n\n\tprotected byte[] getKey() {\n\t\tbyte[] key = WebUtil.getSessionEncryptKey();\n\t\tif (key == null) {\n\t\t\tthrow new BizException(HttpErrorCode.SESSION_KEY_NOT_FOUND, \"加密用的key没有找到\");\n\t\t}\n\t\treturn key;\n\t}\n\n\t@Override\n\tpublic byte[] encrypt(byte[] data, WebContext ctx) throws Exception {\n\t\treturn cipher.encrypt(data, getKey());\n\t}\n\n\t@Override\n\tpublic byte[] decrypt(byte[] data, WebContext ctx) throws Exception {\n\t\treturn cipher.decrypt(data, getKey());\n\t}\n\n\tpublic Encryptor getCipher() {\n\t\treturn cipher;\n\t}\n\n\tpublic void setCipher(Encryptor cipher) {\n\t\tthis.cipher = Objects.requireNonNull(cipher);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/kit/DefaultHttpKit.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.kit;\n\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.yx.common.locale.I18n;\nimport org.yx.conf.AppInfo;\nimport org.yx.http.HttpJson;\nimport org.yx.log.Logs;\nimport org.yx.util.StringUtil;\n\npublic class DefaultHttpKit implements HttpKit {\n\n\tprivate static final int MAX_EXPECT_SIZE = 1024 * 1024;\n\tprivate static final int MIN_EXPECT_SIZE = 64;\n\n\t@Override\n\tpublic Charset charset(HttpServletRequest req) {\n\t\tString charsetName = req.getCharacterEncoding();\n\t\tif (StringUtil.isEmpty(charsetName)) {\n\t\t\treturn HttpSettings.defaultCharset();\n\t\t}\n\t\tif (\"UTF8\".equalsIgnoreCase(charsetName) || \"UTF-8\".equalsIgnoreCase(charsetName)) {\n\t\t\treturn StandardCharsets.UTF_8;\n\t\t}\n\t\tif (!Charset.isSupported(charsetName)) {\n\t\t\tLogs.http().warn(\"charset '{}' is not supported,use default charset {}\", charsetName,\n\t\t\t\t\tHttpSettings.defaultCharset());\n\t\t\treturn HttpSettings.defaultCharset();\n\t\t}\n\t\treturn Charset.forName(charsetName);\n\t}\n\n\t@Override\n\tpublic void sendError(HttpServletResponse resp, String code, String errorMsg, Charset charset) throws IOException {\n\t\tresp.setStatus(HttpSettings.errorHttpStatus());\n\t\tMap<String, Object> map = new LinkedHashMap<>(2, 1);\n\t\tif (AppInfo.getBoolean(\"sumk.http.interrorcode\", false)) {\n\t\t\tint c = 0;\n\t\t\ttry {\n\t\t\t\tc = Integer.valueOf(code);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLogs.http().error(code + \"不能转为int\");\n\t\t\t}\n\t\t\tmap.put(\"code\", c);\n\t\t} else {\n\t\t\tmap.put(\"code\", code);\n\t\t}\n\t\tString i18nMsg = I18n.get(\"sumk.http.error.\" + code, errorMsg);\n\t\tmap.put(\"message\", i18nMsg);\n\t\tresp.getOutputStream().write(HttpJson.operator().toJson(map).getBytes(charset));\n\t}\n\n\t@Override\n\tpublic void setRespHeader(HttpServletResponse resp, Charset charset) throws IOException {\n\t\tresp.setContentType(\"application/json;charset=\" + charset.name());\n\t}\n\n\t@Override\n\tpublic int expectReqDataSize(int expect) {\n\t\tif (expect < 0) {\n\t\t\treturn 1024;\n\t\t}\n\t\tif (expect < MIN_EXPECT_SIZE) {\n\t\t\treturn MIN_EXPECT_SIZE;\n\t\t}\n\t\tif (expect > MAX_EXPECT_SIZE) {\n\t\t\treturn MAX_EXPECT_SIZE;\n\t\t}\n\t\treturn expect;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/kit/HttpCiphers.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.kit;\n\nimport java.util.Objects;\n\nimport org.yx.common.util.S;\nimport org.yx.http.HttpEncryptor;\nimport org.yx.http.Signer;\n\npublic final class HttpCiphers {\n\n\tprivate static HttpEncryptor encryptor = new DefaultHttpEncryptor();\n\n\tpublic static HttpEncryptor getEncryptor() {\n\t\treturn encryptor;\n\t}\n\n\tpublic static void setEncryptor(HttpEncryptor encryptor) {\n\t\tHttpCiphers.encryptor = Objects.requireNonNull(encryptor);\n\t}\n\n\tprivate static Signer signer = (bs, httpServletRequest) -> S.hash().digestByteToString(bs);\n\n\tpublic static Signer getSigner() {\n\t\treturn signer;\n\t}\n\n\tpublic static void setSigner(Signer signer) {\n\t\tHttpCiphers.signer = Objects.requireNonNull(signer);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/kit/HttpKit.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.kit;\n\nimport java.io.IOException;\nimport java.nio.charset.Charset;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic interface HttpKit {\n\n\tCharset charset(HttpServletRequest req);\n\n\tvoid sendError(HttpServletResponse resp, String code, String errorMsg, Charset charset) throws IOException;\n\n\tvoid setRespHeader(HttpServletResponse resp, Charset charset) throws IOException;\n\n\tint expectReqDataSize(int expect);\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/kit/HttpSettings.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.kit;\n\nimport static org.yx.http.server.HttpMethod.DELETE;\nimport static org.yx.http.server.HttpMethod.GET;\nimport static org.yx.http.server.HttpMethod.PATCH;\nimport static org.yx.http.server.HttpMethod.POST;\nimport static org.yx.http.server.HttpMethod.PUT;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic final class HttpSettings {\n\n\tprivate static final String DEFAULT_TEST_KEY = \"thisIsTest\";\n\tprivate static int errorHttpStatus;\n\n\tprivate static long httpSessionTimeoutInMs;\n\tprivate static boolean cookieEnable;\n\tprivate static int maxReqLogSize;\n\tprivate static int maxRespLogSize;\n\tprivate static int warnTime;\n\tprivate static int infoTime;\n\tprivate static Charset defaultCharset = StandardCharsets.UTF_8;\n\tprivate static int maxHttpBody;\n\tprivate static String plainKey;\n\n\tprivate static boolean singleLogin;\n\n\tprivate static String traceHeaderName;\n\n\tprivate static String testKey = DEFAULT_TEST_KEY;\n\n\tprivate static Map<String, String> headers;\n\n\tprivate static List<String> defaultHttpMethods = CollectionUtil.unmodifyList(new String[] { POST, GET });\n\tprivate static final List<String> ALL_HTTP_METHODS = CollectionUtil\n\t\t\t.unmodifyList(new String[] { POST, GET, DELETE, PUT, PATCH });\n\tprivate static List<String> allHttpMethods = ALL_HTTP_METHODS;\n\n\tpublic static List<String> allHttpMethods() {\n\t\treturn allHttpMethods;\n\t}\n\n\tpublic static List<String> defaultHttpMethods() {\n\t\treturn defaultHttpMethods;\n\t}\n\n\tpublic static String testKey() {\n\t\treturn testKey;\n\t}\n\n\tpublic static int errorHttpStatus() {\n\t\treturn errorHttpStatus;\n\t}\n\n\tpublic static int maxHttpBody() {\n\t\treturn maxHttpBody;\n\t}\n\n\tpublic static long httpSessionTimeoutInMs() {\n\t\treturn httpSessionTimeoutInMs;\n\t}\n\n\tpublic static boolean isCookieEnable() {\n\t\treturn cookieEnable;\n\t}\n\n\tpublic static boolean isUploadEnable() {\n\t\treturn AppInfo.getBoolean(\"sumk.http.upload.enable\", true);\n\t}\n\n\tpublic static int maxReqLogSize() {\n\t\treturn maxReqLogSize;\n\t}\n\n\tpublic static int maxRespLogSize() {\n\t\treturn maxRespLogSize;\n\t}\n\n\tpublic static int warnTime() {\n\t\treturn warnTime;\n\t}\n\n\tpublic static int infoTime() {\n\t\treturn infoTime;\n\t}\n\n\tpublic static boolean isSingleLogin() {\n\t\treturn singleLogin;\n\t}\n\n\tpublic static boolean allowPlain(HttpServletRequest request) {\n\t\tString plainKey = HttpSettings.plainKey;\n\t\treturn plainKey != null && plainKey.equals(request.getParameter(\"_plainKey\"));\n\t}\n\n\tpublic static Map<String, String> responseHeaders() {\n\t\treturn headers;\n\t}\n\n\tstatic List<String> splitAndTrim(String v) {\n\t\tList<String> list = StringUtil.splitAndTrim(v, \",\", \";\");\n\t\treturn CollectionUtil.unmodifyList(list.toArray(new String[list.size()]));\n\t}\n\n\tpublic static void init() {\n\t\tHttpSettings.errorHttpStatus = AppInfo.getInt(\"sumk.http.errorcode\", 550);\n\t\tString c = AppInfo.get(\"sumk.http.charset\");\n\t\tif (StringUtil.isNotEmpty(c)) {\n\t\t\ttry {\n\t\t\t\tHttpSettings.defaultCharset = Charset.forName(c);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLogs.http().error(\"{}不是有效的字符集编码\", c);\n\t\t\t}\n\n\t\t}\n\t\tString methods = AppInfo.getLatin(\"sumk.http.method.default\", null);\n\t\tif (methods != null) {\n\t\t\tdefaultHttpMethods = splitAndTrim(methods);\n\t\t}\n\t\tmethods = null;\n\t\tAppInfo.addObserver(info -> {\n\t\t\tHttpSettings.maxReqLogSize = AppInfo.getInt(\"sumk.http.log.reqsize\", 1000);\n\t\t\tHttpSettings.maxRespLogSize = AppInfo.getInt(\"sumk.http.log.respsize\", 5000);\n\t\t\tHttpSettings.warnTime = AppInfo.getInt(\"sumk.http.log.warn.time\", 3000);\n\t\t\tHttpSettings.infoTime = AppInfo.getInt(\"sumk.http.log.info.time\", 1000);\n\t\t\tHttpSettings.maxHttpBody = AppInfo.getInt(\"sumk.http.body.maxLength\", 1024 * 1024 * 100);\n\t\t\tHttpSettings.singleLogin = AppInfo.getBoolean(\"sumk.http.session.single\", false);\n\t\t\tString plain = AppInfo.get(\"sumk.http.plain.key\", null);\n\t\t\tHttpSettings.plainKey = \"\".equals(plain) ? null : plain;\n\n\t\t\tHttpSettings.cookieEnable = AppInfo.getBoolean(\"sumk.http.header.usecookie\", true);\n\t\t\tHttpSettings.httpSessionTimeoutInMs = 1000L * AppInfo.getInt(\"sumk.http.session.timeout\", 60 * 30);\n\t\t\tHttpSettings.traceHeaderName = AppInfo.get(\"sumk.http.header.trace\", \"s-trace\");\n\n\t\t\tMap<String, String> map = CollectionUtil.unmodifyMap(AppInfo.subMap(\"s.http.response.header.\"));\n\t\t\tHttpSettings.headers = map.isEmpty() ? null : map;\n\t\t\tHttpSettings.testKey = AppInfo.get(\"sumk.http.testkey\", DEFAULT_TEST_KEY);\n\t\t\tString ms = AppInfo.getLatin(\"sumk.http.method.all\", null);\n\t\t\tHttpSettings.allHttpMethods = ms == null ? ALL_HTTP_METHODS : splitAndTrim(ms);\n\t\t});\n\t}\n\n\tpublic static Charset defaultCharset() {\n\t\treturn defaultCharset;\n\t}\n\n\tpublic static String traceHeaderName() {\n\t\treturn traceHeaderName;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/kit/InnerHttpUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.kit;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Objects;\nimport java.util.function.BiConsumer;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.yx.base.context.ActionContext;\nimport org.yx.base.context.AppContext;\nimport org.yx.base.sumk.UnsafeByteArrayOutputStream;\nimport org.yx.common.action.ActionStatis;\nimport org.yx.common.action.ActionStatisImpl;\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.BizException;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.MessageType;\nimport org.yx.http.WebUtil;\nimport org.yx.log.Logs;\nimport org.yx.util.UUIDSeed;\n\npublic final class InnerHttpUtil {\n\tprivate static HttpKit kit = new DefaultHttpKit();\n\tprivate static ActionStatis actStatis = new ActionStatisImpl();\n\tprivate static BiConsumer<HttpServletRequest, HttpServletResponse> optionMethodHandler;\n\n\tpublic static BiConsumer<HttpServletRequest, HttpServletResponse> getOptionMethodHandler() {\n\t\treturn optionMethodHandler;\n\t}\n\n\tpublic static void setOptionMethodHandler(BiConsumer<HttpServletRequest, HttpServletResponse> optionMethodHandler) {\n\t\tInnerHttpUtil.optionMethodHandler = optionMethodHandler;\n\t}\n\n\tpublic static void setActionStatis(ActionStatis actStatis) {\n\t\tInnerHttpUtil.actStatis = Objects.requireNonNull(actStatis);\n\t}\n\n\tpublic static HttpKit getKit() {\n\t\treturn kit;\n\t}\n\n\tpublic static void setKit(HttpKit kit) {\n\t\tInnerHttpUtil.kit = Objects.requireNonNull(kit);\n\t}\n\n\tpublic static byte[] extractData(InputStream in, int expectSize) throws IOException {\n\t\tint count = 0;\n\t\tint n = 0;\n\t\texpectSize = kit.expectReqDataSize(expectSize);\n\t\tif (Logs.http().isTraceEnabled()) {\n\t\t\tLogs.http().trace(\"expect request content length: {}\", expectSize);\n\t\t}\n\t\tbyte[] temp = new byte[512];\n\t\t@SuppressWarnings(\"resource\")\n\t\tUnsafeByteArrayOutputStream output = new UnsafeByteArrayOutputStream(expectSize);\n\t\twhile (-1 != (n = in.read(temp))) {\n\t\t\toutput.write(temp, 0, n);\n\t\t\tcount += n;\n\t\t\tif (count > HttpSettings.maxHttpBody()) {\n\t\t\t\tthrow BizException.create(HttpErrorCode.BODY_TOO_BIG, \"请求数据太长\");\n\t\t\t}\n\t\t}\n\t\tbyte[] bs = output.extractHttpBodyData();\n\t\toutput.close();\n\t\treturn bs;\n\t}\n\n\tpublic static Charset charset(HttpServletRequest req) {\n\t\treturn kit.charset(req);\n\t}\n\n\tpublic static void record(String act, long time, boolean isSuccess) {\n\t\tactStatis.visit(act, time, isSuccess);\n\t}\n\n\tpublic static ActionStatis getActionStatis() {\n\t\treturn actStatis;\n\t}\n\n\tpublic static void sendError(HttpServletResponse resp, int code, String errorMsg, Charset charset) {\n\t\tsendError(resp, String.valueOf(code), errorMsg, charset);\n\t}\n\n\tpublic static void sendError(HttpServletResponse resp, String code, String message, Charset charset) {\n\t\ttry {\n\t\t\tkit.sendError(resp, code, message, charset);\n\t\t} catch (IOException e) {\n\t\t\tLogs.http().error(e.getLocalizedMessage(), e);\n\t\t}\n\t}\n\n\tpublic static void setRespHeader(HttpServletResponse resp, Charset charset) throws IOException {\n\t\tkit.setRespHeader(resp, charset);\n\t}\n\n\tpublic static boolean preServerHandle(HttpServletRequest req, HttpServletResponse resp, String firstKey) {\n\t\tresp.setContentType(\"text/plain;charset=UTF-8\");\n\t\tString md5 = AppInfo.get(firstKey, \"sumk.union.monitor\",\n\t\t\t\t\"b914914b9850d74435056a37cdd915f2b0fb7e23401034cf8579676ff2cce7db\");\n\n\t\tString sign = WebUtil.fromHeaderOrParamter(req, \"sign\");\n\t\tif (sign == null) {\n\t\t\tLogs.http().debug(\"sign is empty\");\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tString signed = S.hash().digest(sign, StandardCharsets.UTF_8);\n\t\t\tif (!md5.equalsIgnoreCase(signed)) {\n\t\t\t\tLogs.http().debug(\"signed:{},need:{}\", signed, md5);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static void startContext(HttpServletRequest req, HttpServletResponse resp, String act) {\n\t\tString traceId = UUIDSeed.seq18();\n\t\tString pre = req.getHeader(\"trace-action\");\n\t\tif (pre != null && pre.length() > 0 && AppInfo.getBoolean(\"sumk.http.traceid.pre.allow\", true)) {\n\t\t\ttraceId = String.join(\".\", pre, traceId);\n\t\t}\n\t\tActionContext.newContext(act, traceId,\n\t\t\t\tAppContext.inst().isTest() && \"1\".equals(req.getParameter(HttpSettings.testKey())));\n\t\tresp.setHeader(HttpSettings.traceHeaderName(), traceId);\n\t}\n\n\tpublic static MessageType parseMessageType(String name) {\n\t\tif (name == null || name.isEmpty()) {\n\t\t\treturn MessageType.PLAIN;\n\t\t}\n\t\tname = name.toUpperCase();\n\t\tif (name.equals(\"ENCRYPT_BASE64\") || name.equals(\"ENCRYPTBASE64\")) {\n\t\t\treturn MessageType.ENCRYPT_BASE64;\n\t\t}\n\t\tif (name.equals(\"BASE64\")) {\n\t\t\treturn MessageType.BASE64;\n\t\t}\n\t\tif (name.equals(\"ENCRYPT\")) {\n\t\t\treturn MessageType.ENCRYPT;\n\t\t}\n\t\tLogs.http().warn(\"配置值{}对应的MessageType是PLAIN\", name);\n\t\treturn MessageType.PLAIN;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/kit/LocalWebContext.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.kit;\n\nimport org.yx.http.handler.WebContext;\n\npublic final class LocalWebContext {\n\tprivate static final ThreadLocal<WebContext> CTX = new ThreadLocal<>();\n\n\tpublic static WebContext getCtx() {\n\t\treturn CTX.get();\n\t}\n\n\tpublic static void setCtx(WebContext ctx) {\n\t\tCTX.set(ctx);\n\t}\n\n\tpublic static void remove() {\n\t\tCTX.remove();\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/log/HttpLogHandler.java",
    "content": "package org.yx.http.log;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.yx.http.handler.WebContext;\n\n/**\n * 这个在用户线程里执行，可以取到线程变量。处理最好用异步，要不然会阻塞请求，影响用户体验\n */\npublic interface HttpLogHandler {\n\n\tvoid log(WebContext ctx, HttpServletRequest req, Throwable ex, long time);\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/log/HttpLogs.java",
    "content": "package org.yx.http.log;\n\nimport java.nio.charset.Charset;\nimport java.util.Objects;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.yx.http.HttpJson;\nimport org.yx.http.handler.WebContext;\nimport org.yx.log.LogKits;\n\npublic class HttpLogs {\n\tprivate static HttpLogHandler handler = new PlainHttpLogHandler();\n\n\tpublic static HttpLogHandler getHandler() {\n\t\treturn handler;\n\t}\n\n\tpublic static void setHandler(HttpLogHandler handler) {\n\t\tHttpLogs.handler = Objects.requireNonNull(handler);\n\t}\n\n\tpublic static void log(WebContext ctx, HttpServletRequest req, Throwable ex, long time) {\n\t\thandler.log(ctx, req, ex, time);\n\t}\n\n\tpublic static String getParam(WebContext wc, int maxLength) {\n\t\tif (wc == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn parse(wc.dataInString(), wc.data(), maxLength, wc.charset());\n\t}\n\n\tpublic static String getResponse(WebContext wc, int maxLength) {\n\t\tif (wc == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn parse(wc.respInString(), wc.result(), maxLength, wc.charset());\n\t}\n\n\tpublic static String parse(String data, Object obj, int maxLength, Charset charset) {\n\t\tif (data != null) {\n\t\t\treturn LogKits.shorterSubfix(data, maxLength);\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (obj.getClass() == byte[].class) {\n\t\t\tbyte[] bs = (byte[]) obj;\n\t\t\tint len = bs.length;\n\t\t\tif (len > maxLength * 3) {\n\t\t\t\tlen = maxLength * 3;\n\t\t\t}\n\t\t\tString temp = new String(bs, 0, len, charset);\n\t\t\treturn LogKits.shorterSubfix(temp, maxLength);\n\t\t}\n\t\tString resp = HttpJson.operator().toJson(obj);\n\t\treturn LogKits.shorterSubfix(resp, maxLength);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/log/PlainHttpLogHandler.java",
    "content": "package org.yx.http.log;\n\nimport static org.yx.conf.AppInfo.LN;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.slf4j.Logger;\nimport org.yx.exception.BizException;\nimport org.yx.http.act.HttpActions;\nimport org.yx.http.handler.WebContext;\nimport org.yx.http.kit.HttpSettings;\nimport org.yx.log.Log;\nimport org.yx.util.StringUtil;\n\npublic class PlainHttpLogHandler implements HttpLogHandler {\n\tprotected Logger log = Log.get(\"sumk.http.log\");\n\n\t@Override\n\tpublic void log(WebContext ctx, HttpServletRequest req, Throwable ex, final long time) {\n\t\tif (ex != null && log.isErrorEnabled()) {\n\t\t\tif (ctx != null) {\n\t\t\t\tlogError(ctx, ex, time);\n\t\t\t} else {\n\t\t\t\tlogError(req, ex, time);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (time >= HttpSettings.warnTime() && log.isWarnEnabled()) {\n\t\t\tlog.warn(buildLogMsg(ctx, req, time));\n\t\t\treturn;\n\t\t}\n\n\t\tif (time >= HttpSettings.infoTime() && log.isInfoEnabled()) {\n\t\t\tlog.info(buildLogMsg(ctx, req, time));\n\t\t\treturn;\n\t\t}\n\n\t\tif (log.isDebugEnabled()) {\n\t\t\tlog.debug(buildLogMsg(ctx, req, time));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprotected void logError(HttpServletRequest req, Throwable ex, long time) {\n\t\tStringBuilder sb = newStringBuilder(req);\n\t\tsb.append(req.getRequestURI()).append(\"   remote:\").append(remoteAddr(req)).append(\"   time:\").append(time);\n\t\tlogError(sb.toString(), ex);\n\t}\n\n\tprotected void logError(WebContext wc, Throwable ex, long time) {\n\t\tStringBuilder sb = newStringBuilder(wc.httpRequest());\n\t\tsb.append(getRawAct(wc)).append(\"   remote:\").append(remoteAddr(wc.httpRequest())).append(\"   time:\")\n\t\t\t\t.append(time).append(LN).append(\"   param: \").append(getParam(wc, HttpSettings.maxReqLogSize()));\n\t\tlogError(sb.toString(), ex);\n\t}\n\n\tprotected void logError(String msg, Throwable ex) {\n\t\tif (ex instanceof BizException) {\n\t\t\tlog.warn(msg, ex);\n\t\t\treturn;\n\t\t}\n\t\tlog.error(msg, ex);\n\t}\n\n\tprotected StringBuilder newStringBuilder(HttpServletRequest req) {\n\t\treturn new StringBuilder(64).append(\"[\").append(req.getMethod()).append(\"] \");\n\t}\n\n\tprotected String buildLogMsg(WebContext wc, HttpServletRequest req, long time) {\n\t\tStringBuilder sb = newStringBuilder(req);\n\t\tif (wc != null) {\n\t\t\tsb.append(getRawAct(wc)).append(\"   remote:\").append(remoteAddr(req)).append(\"   time:\").append(time)\n\t\t\t\t\t.append(LN).append(\"   param: \").append(getParam(wc, HttpSettings.maxReqLogSize())).append(LN)\n\t\t\t\t\t.append(\"   resp: \").append(getResponse(wc, HttpSettings.maxRespLogSize()));\n\t\t\treturn sb.toString();\n\t\t}\n\t\tsb.append(req.getRequestURI()).append(\"   time:\").append(time);\n\t\treturn sb.toString();\n\t}\n\n\tprotected String remoteAddr(HttpServletRequest req) {\n\t\tString ip = req.getHeader(\"X-Real-IP\");\n\t\treturn StringUtil.isNotEmpty(ip) ? ip : req.getRemoteAddr();\n\t}\n\n\tprotected String getParam(WebContext wc, int maxLength) {\n\t\treturn HttpLogs.getParam(wc, maxLength);\n\t}\n\n\tprotected String getResponse(WebContext wc, int maxLength) {\n\t\treturn HttpLogs.getResponse(wc, maxLength);\n\t}\n\n\tprotected String getRawAct(WebContext wc) {\n\t\tString act = wc.rawAct();\n\t\treturn act.endsWith(HttpActions.PREFIX_MATCH_ENDING) ? wc.httpRequest().getPathInfo() : act;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/monitor/HttpMonitors.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.monitor;\n\nimport static org.yx.common.monitor.Monitors.BLANK;\n\nimport static org.yx.conf.AppInfo.LN;\n\nimport java.lang.management.GarbageCollectorMXBean;\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.MemoryPoolMXBean;\nimport java.text.DecimalFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.TreeMap;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Predicate;\n\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.base.matcher.Matchers;\nimport org.yx.bean.InnerIOC;\nimport org.yx.bean.NameSlot;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.log.LogLevel;\nimport org.yx.log.Loggers;\nimport org.yx.main.SumkServer;\nimport org.yx.util.SumkDate;\n\npublic final class HttpMonitors {\n\n\tpublic static String serverInfo() {\n\t\tlong startTime = SumkServer.startTime();\n\t\tlong now = System.currentTimeMillis();\n\t\tlong ms = now - startTime;\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"start\").append(BLANK).append(SumkDate.of(startTime).to_yyyy_MM_dd_HH_mm_ss_SSS().replace(\" \", \"T\"))\n\t\t\t\t.append(BLANK).append(\"run(ms)\").append(BLANK).append(ms).append(LN).append(\"localip\").append(BLANK)\n\t\t\t\t.append(AppInfo.getLocalIp()).append(BLANK).append(\"pid\").append(BLANK).append(AppInfo.pid()).append(LN)\n\t\t\t\t.append(\"framework\").append(BLANK).append(Const.sumkVersion());\n\t\tString v = AppInfo.appId(null);\n\t\tif (v != null) {\n\t\t\tsb.append(BLANK).append(\"appId\").append(BLANK).append(v);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String systemInfo() {\n\t\tMap<Object, Object> map = new HashMap<>(System.getProperties());\n\t\tStringBuilder sb = new StringBuilder();\n\t\tmap.forEach((k, v) -> {\n\t\t\tif (v != null) {\n\t\t\t\tv = v.toString().replace(\"\\r\", \"\\\\r\").replace(\"\\n\", \"\\\\n\");\n\t\t\t}\n\t\t\tsb.append(k).append(\" : \").append(v).append(LN);\n\t\t});\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String jvmInfo() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"## name   init   max   commited    used\").append(LN);\n\t\tDecimalFormat f = new DecimalFormat(\"#,###\");\n\t\tList<MemoryPoolMXBean> mpmxbs = ManagementFactory.getMemoryPoolMXBeans();\n\t\tfor (MemoryPoolMXBean mpmxb : mpmxbs) {\n\t\t\tif (mpmxb == null || mpmxb.getUsage() == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString name = mpmxb.getName();\n\t\t\tif (name == null || name.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsb.append(name).append(BLANK).append(f.format(mpmxb.getUsage().getInit())).append(BLANK)\n\t\t\t\t\t.append(f.format(mpmxb.getUsage().getMax())).append(BLANK)\n\t\t\t\t\t.append(f.format(mpmxb.getUsage().getCommitted())).append(BLANK)\n\t\t\t\t\t.append(f.format(mpmxb.getUsage().getUsed())).append(LN);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String stack(boolean full) {\n\t\tPredicate<String> ignore = full ? BooleanMatcher.FALSE\n\t\t\t\t: Matchers.createWildcardMatcher(\n\t\t\t\t\t\t\"org.yx.*,java.*,javax.*,sun.*,org.eclipse.jetty.*,org.apache.zookeeper.*,io.netty.*,org.apache.mina.*\");\n\t\tMap<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();\n\t\tStringBuilder all = new StringBuilder(2000).append(\"线程总数:\").append(map.size()).append(LN);\n\t\tfor (Entry<Thread, StackTraceElement[]> en : map.entrySet()) {\n\t\t\tThread t = en.getKey();\n\t\t\tStackTraceElement[] st = en.getValue();\n\t\t\tboolean matched = false;\n\t\t\tStringBuilder sb = new StringBuilder(256).append(t.getName()).append(\"  [id:\").append(t.getId()).append(\"]\")\n\t\t\t\t\t.append(BLANK).append(t.getState()).append(LN);\n\t\t\tboolean first = true;\n\t\t\tfor (StackTraceElement e : st) {\n\t\t\t\tif (first) {\n\t\t\t\t\tfirst = false;\n\t\t\t\t} else {\n\t\t\t\t\tsb.append(BLANK);\n\t\t\t\t}\n\t\t\t\tif (!ignore.test(e.getClassName())) {\n\t\t\t\t\tmatched = true;\n\t\t\t\t}\n\t\t\t\tsb.append(e.getClassName()).append('.').append(e.getMethodName());\n\t\t\t\tif (e.getLineNumber() > 0) {\n\t\t\t\t\tsb.append(\" \").append(e.getLineNumber());\n\t\t\t\t}\n\t\t\t\tsb.append(LN);\n\t\t\t}\n\t\t\tif (matched) {\n\t\t\t\tall.append(sb.append(LN));\n\t\t\t}\n\t\t}\n\t\treturn all.toString();\n\t}\n\n\tpublic static String threadPoolInfo(ThreadPoolExecutor pool) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"active\").append(BLANK).append(pool.getActiveCount()).append(BLANK).append(\"size\").append(BLANK)\n\t\t\t\t.append(pool.getPoolSize()).append(BLANK).append(\"queue\").append(BLANK).append(pool.getQueue().size())\n\t\t\t\t.append(BLANK).append(LN).append(\"max\").append(BLANK).append(pool.getMaximumPoolSize()).append(BLANK)\n\t\t\t\t.append(\"keepAlive(ms)\").append(BLANK).append(pool.getKeepAliveTime(TimeUnit.MILLISECONDS))\n\t\t\t\t.append(BLANK)\n\n\t\t\t\t.append(\"completed*\").append(BLANK).append(pool.getCompletedTaskCount());\n\t\treturn sb.toString();\n\t}\n\n\tpublic static List<String> beans() {\n\t\tCollection<Object> beans = InnerIOC.beans();\n\t\tList<String> names = new ArrayList<>();\n\t\tfor (Object obj : beans) {\n\t\t\tnames.add(obj.getClass().getName());\n\t\t}\n\t\tCollections.sort(names);\n\t\treturn names;\n\t}\n\n\tpublic static String beansName() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tList<String> names = InnerIOC.beanNames();\n\t\tCollections.sort(names);\n\t\tfor (String name : names) {\n\t\t\tNameSlot slot = InnerIOC.getSlot(name);\n\t\t\tsb.append(slot).append(Const.LN);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String logLevels() {\n\t\tMap<String, LogLevel> map = new TreeMap<>(Loggers.currentLevels());\n\t\tStringBuilder sb = new StringBuilder(\"#logLevels:\").append(LN);\n\t\tchar[] black = new char[7];\n\t\tArrays.fill(black, ' ');\n\t\tmap.forEach((k, v) -> {\n\t\t\tsb.append(v).append(black, 0, black.length - v.name().length()).append(k).append(LN);\n\t\t});\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String sumkDateCacheChangeCount() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"##sumkDateCached\").append(BLANK).append(SumkDate.cacheChangeCount());\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String gcInfo() {\n\t\tStringBuilder sb = new StringBuilder(64).append(\"##name\").append(BLANK).append(\"count\").append(BLANK)\n\t\t\t\t.append(\"time(ms)\");\n\t\tfor (GarbageCollectorMXBean mxBean : ManagementFactory.getGarbageCollectorMXBeans()) {\n\t\t\tsb.append(LN).append(getGcName(mxBean.getName())).append(BLANK).append(mxBean.getCollectionCount())\n\t\t\t\t\t.append(BLANK).append(mxBean.getCollectionTime());\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate static String getGcName(String name) {\n\t\tif (\"PS Scavenge\".equals(name) || \"ParNew\".equals(name) || \"G1 Young Generation\".equals(name)\n\t\t\t\t|| \"Copy\".equals(name)) {\n\t\t\treturn \"Young\";\n\t\t}\n\t\tif (\"PS MarkSweep\".equals(name) || \"ConcurrentMarkSweep\".equals(name) || \"G1 Old Generation\".equals(name)\n\t\t\t\t|| \"MarkSweepCompact\".equals(name)) {\n\t\t\treturn \"Old\";\n\t\t}\n\t\treturn name;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/select/DefaultHttpActionSelector.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.select;\n\nimport static org.yx.base.matcher.Matchers.WILDCARD;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport org.yx.common.StringEntity;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.http.act.HttpActionInfo;\nimport org.yx.http.act.HttpActionNode;\nimport org.yx.http.act.HttpActions;\nimport org.yx.http.act.IgnoreNameActionInfo;\nimport org.yx.http.act.PrefixMappingActionInfo;\nimport org.yx.log.Logs;\n\npublic class DefaultHttpActionSelector implements HttpActionSelector {\n\n\tprivate Map<String, HttpActionInfo[]> actMap = Collections.emptyMap();\n\tprivate HttpActionInfo[] starts;\n\tprivate HttpActionInfo[] lastMappings;\n\n\t@Override\n\tpublic HttpActionInfo getHttpInfo(String act, String method) {\n\t\tmethod = method.toUpperCase();\n\t\tHttpActionInfo info = this.getAcceptedAction(actMap.get(act), act, method);\n\t\tif (info != null) {\n\t\t\treturn info;\n\t\t}\n\n\t\tinfo = this.getAcceptedAction(this.starts, act, method);\n\t\tif (info != null) {\n\t\t\treturn info;\n\t\t}\n\n\t\treturn this.getAcceptedAction(this.lastMappings, act, method);\n\t}\n\n\tprotected HttpActionInfo getAcceptedAction(HttpActionInfo[] infos, String act, String method) {\n\t\tif (infos == null) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (HttpActionInfo info : infos) {\n\t\t\tif (info.match(act, method)) {\n\t\t\t\treturn info;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Collection<HttpActionInfo> actions() {\n\t\tList<HttpActionInfo> list = new ArrayList<>(200);\n\t\tfor (HttpActionInfo[] infos : this.actMap.values()) {\n\t\t\tfor (HttpActionInfo info : infos) {\n\t\t\t\tlist.add(info);\n\t\t\t}\n\t\t}\n\t\tlist.sort(null);\n\n\t\tif (this.starts != null) {\n\t\t\tlist.addAll(Arrays.asList(this.starts));\n\t\t}\n\t\tif (this.lastMappings != null) {\n\t\t\tlist.addAll(Arrays.asList(this.lastMappings));\n\t\t}\n\t\treturn list;\n\t}\n\n\tprotected boolean isDuplicate(HttpActionInfo info, HttpActionInfo[] old) {\n\t\tfor (String m : info.node().methods()) {\n\t\t\tfor (HttpActionInfo o : old) {\n\t\t\t\tif (o.node().acceptMethod(m)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprotected void appendStart(PrefixMappingActionInfo info, List<PrefixMappingActionInfo> startList) {\n\t\tfor (PrefixMappingActionInfo old : startList) {\n\t\t\tif (!Objects.equals(info.getUrlStart(), old.getUrlStart())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.urlDuplicate(info.formalName());\n\t\t\tfor (String m : info.node().methods()) {\n\t\t\t\tif (old.node().acceptMethod(m)) {\n\t\t\t\t\tthrow new SumkException(345647432, \"web接口\" + old.formalName() + \"重复了,http方法是\" + m);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tstartList.add(info);\n\t}\n\n\tprotected void urlDuplicate(String url) {\n\t\tString key = \"sumk.http.url.duplicate\";\n\t\tif (!AppInfo.getBoolean(key, false)) {\n\t\t\tthrow new SumkException(345647431, \"web接口\" + url + \"重复了，设置\" + key + \"=1可以开启url重名功能\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void init(List<StringEntity<HttpActionNode>> infos, Function<String, String> nameResolver) {\n\t\tMap<String, HttpActionInfo[]> actMap = new HashMap<>(infos.size() * 2);\n\t\tList<PrefixMappingActionInfo> startList = new ArrayList<>();\n\t\tfor (StringEntity<HttpActionNode> kv : infos) {\n\t\t\tString parsedName = nameResolver.apply(kv.key());\n\t\t\tif (parsedName.endsWith(HttpActions.PREFIX_MATCH_ENDING)) {\n\t\t\t\tthis.appendStart(new PrefixMappingActionInfo(kv.key(), kv.value(), parsedName,\n\t\t\t\t\t\tparsedName.substring(0, parsedName.length() - 1)), startList);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tHttpActionInfo info = new IgnoreNameActionInfo(kv.key(), kv.value(), parsedName);\n\t\t\tHttpActionInfo[] old = actMap.get(parsedName);\n\t\t\tif (old == null) {\n\t\t\t\tactMap.put(parsedName, new HttpActionInfo[] { info });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tthis.urlDuplicate(parsedName);\n\t\t\tif (this.isDuplicate(info, old)) {\n\t\t\t\tthrow new SumkException(345647432, \"web接口\" + parsedName + \"重复了\");\n\t\t\t}\n\t\t\tHttpActionInfo[] newInfos = Arrays.copyOf(old, old.length + 1);\n\t\t\tnewInfos[newInfos.length - 1] = info;\n\t\t\tactMap.put(parsedName, newInfos);\n\t\t}\n\n\t\tif (startList.size() > 0) {\n\t\t\tString key = \"sumk.http.url.match.prefix\";\n\t\t\tif (!AppInfo.getBoolean(key, false)) {\n\t\t\t\tLogs.http().error(\"{}接口是前缀匹配，但是本系统没有开启前缀匹配功能。前缀匹配接口共有{}个\", startList.get(0).rawAct(), startList.size());\n\t\t\t\tthrow new SumkException(345647431, \"需要设置\" + key + \"=1才能开启前缀匹配的功能\");\n\t\t\t}\n\t\t}\n\t\tstartList.sort(null);\n\t\tCollections.reverse(startList);\n\t\tthis.lastMappings = actMap.remove(WILDCARD);\n\t\tthis.actMap = new HashMap<>(actMap);\n\t\tthis.starts = startList.isEmpty() ? null : startList.toArray(new PrefixMappingActionInfo[startList.size()]);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/select/HttpActionSelector.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.select;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.yx.common.StringEntity;\nimport org.yx.http.act.HttpActionInfo;\nimport org.yx.http.act.HttpActionNode;\n\npublic interface HttpActionSelector {\n\n\t/**\n\t * 用于初始化，在sumk启动的时候调用一次。\n\t * \n\t * @param infos        里面的key是解析后的act，跟@Web中定义的格式可能不同\n\t * @param nameResolver 名称解析器\n\t */\n\tvoid init(List<StringEntity<HttpActionNode>> infos, Function<String, String> nameResolver);\n\n\t/**\n\t * 根据真正的act获取HttpActionInfo。对于url含参数等场景，可以返回HttpActionInfo子类\n\t * \n\t * @param act    解析后的act\n\t * @param method http请求的方法，比如GET、POST，参加HttpMethod接口\n\t * @return 如果没找到，可以返回默认servlet，也可以返回null\n\t */\n\tHttpActionInfo getHttpInfo(String act, String method);\n\n\t/**\n\t * 里面的接口是不重复的，但可能存在name一样，method不一样的情况。 为了便于查看，建议对里面的顺序做排序\n\t * \n\t * @return 返回所有的接口列表，逗号隔开的会被当成多个。\n\t */\n\tCollection<HttpActionInfo> actions();\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/server/AbstractActionServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.server;\n\nimport java.io.IOException;\nimport java.lang.reflect.InvocationTargetException;\nimport java.nio.charset.Charset;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.slf4j.Logger;\nimport org.yx.base.context.ActionContext;\nimport org.yx.common.locale.I18n;\nimport org.yx.common.validate.InvalidParamException;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.BizException;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.act.HttpActionInfo;\nimport org.yx.http.act.HttpActions;\nimport org.yx.http.handler.WebContext;\nimport org.yx.http.kit.InnerHttpUtil;\nimport org.yx.http.kit.LocalWebContext;\nimport org.yx.http.log.HttpLogs;\nimport org.yx.log.Logs;\nimport org.yx.util.StringUtil;\n\npublic abstract class AbstractActionServer extends AbstractCommonHttpServlet {\n\n\tprivate static final long serialVersionUID = 74378082364534491L;\n\n\tprotected final Logger log = Logs.http();\n\n\t@Override\n\tprotected void handle(HttpServletRequest req, HttpServletResponse resp) {\n\t\tfinal long beginTime = System.currentTimeMillis();\n\t\tThrowable ex = null;\n\t\tWebContext wc = null;\n\t\ttry {\n\t\t\tfinal Charset charset = InnerHttpUtil.charset(req);\n\t\t\tthis.setRespHeader(req, resp, charset);\n\n\t\t\tString rawAct = getRawAct(req);\n\t\t\tif (log.isTraceEnabled()) {\n\t\t\t\tlog.trace(\"raw act={}\", rawAct);\n\t\t\t}\n\t\t\tif (rawAct == null || rawAct.isEmpty()) {\n\t\t\t\tlog.error(\"raw act is empty in {}?{}\", req.getPathInfo(), req.getQueryString());\n\t\t\t\tthrow BizException.create(HttpErrorCode.ACT_FORMAT_ERROR,\n\t\t\t\t\t\tI18n.get(\"sumk.http.error.actformat\", \"{0}请求格式不正确\", rawAct));\n\t\t\t}\n\t\t\tHttpActionInfo info = HttpActions.getHttpInfo(rawAct, req.getMethod());\n\t\t\tif (info == null) {\n\t\t\t\tlog.error(\"{} is not action\", rawAct);\n\t\t\t\tthrow BizException.create(HttpErrorCode.ACT_NOT_FOUND,\n\t\t\t\t\t\tI18n.get(\"sumk.http.error.act.notfound\", \"接口不存在\"));\n\t\t\t}\n\t\t\trawAct = info.rawAct();\n\t\t\tif (info.node().overflowThreshold()) {\n\t\t\t\tthrow BizException.create(HttpErrorCode.THREAD_THRESHOLD_OVER, \"系统限流降级\");\n\t\t\t}\n\t\t\tInnerHttpUtil.startContext(req, resp, rawAct);\n\t\t\twc = new WebContext(info, req, resp, charset);\n\t\t\tLocalWebContext.setCtx(wc);\n\t\t\thandle(wc);\n\t\t} catch (Throwable e) {\n\t\t\ttry {\n\t\t\t\tex = handleError(req, resp, e);\n\t\t\t} catch (Exception e2) {\n\t\t\t\tex = e;\n\t\t\t\tlog.error(\"处理异常发生错误。可能是网络问题，也可能是异常处理出问题(不该发生)\", e2);\n\t\t\t}\n\t\t} finally {\n\t\t\tlong time = System.currentTimeMillis() - beginTime;\n\t\t\tHttpLogs.log(wc, req, ex, time);\n\t\t\tif (wc != null) {\n\n\t\t\t\tInnerHttpUtil.record(wc.rawAct(), time, ex == null && !wc.isFailed());\n\t\t\t}\n\t\t\tLocalWebContext.remove();\n\t\t\tActionContext.remove();\n\t\t}\n\t}\n\n\tprotected void sendError(HttpServletRequest req, HttpServletResponse resp, String code, String errorMsg) {\n\t\tInnerHttpUtil.sendError(resp, code, errorMsg, InnerHttpUtil.charset(req));\n\t}\n\n\tprotected String getRawAct(HttpServletRequest req) {\n\t\tString act = req.getPathInfo();\n\t\tif (StringUtil.isEmpty(act) && AppInfo.getBoolean(\"sumk.http.act.query\", false)) {\n\t\t\tact = req.getParameter(\"_act\");\n\t\t\tif (act != null) {\n\t\t\t\tact = act.replace('.', '/');\n\t\t\t}\n\t\t}\n\t\treturn act;\n\t}\n\n\tprotected void setRespHeader(HttpServletRequest req, HttpServletResponse resp, Charset charset) throws IOException {\n\t\tInnerHttpUtil.setRespHeader(resp, charset);\n\t}\n\n\tprotected abstract void handle(WebContext wc) throws Throwable;\n\n\tprotected Throwable handleError(HttpServletRequest req, HttpServletResponse resp, Throwable e) {\n\t\tThrowable temp = e;\n\t\tif (temp instanceof InvocationTargetException) {\n\t\t\ttemp = ((InvocationTargetException) temp).getTargetException();\n\t\t}\n\t\tif (temp instanceof InvalidParamException) {\n\t\t\tsendError(req, resp, String.valueOf(HttpErrorCode.VALIDATE_ERROR), temp.getMessage());\n\t\t\treturn temp;\n\t\t}\n\t\tThrowable root = temp;\n\t\tdo {\n\t\t\tif (temp instanceof BizException) {\n\t\t\t\tBizException be = (BizException) temp;\n\t\t\t\tsendError(req, resp, be.getCode(), be.getMessage());\n\t\t\t\treturn be;\n\t\t\t}\n\t\t} while ((temp = temp.getCause()) != null);\n\n\t\tsendError(req, resp, String.valueOf(HttpErrorCode.HANDLE_ERROR), \"请求处理异常\");\n\t\treturn root;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/server/AbstractCommonHttpServlet.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.server;\n\nimport java.io.IOException;\nimport java.util.Map.Entry;\nimport java.util.function.BiConsumer;\n\nimport javax.servlet.GenericServlet;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.kit.HttpSettings;\nimport org.yx.http.kit.InnerHttpUtil;\n\npublic abstract class AbstractCommonHttpServlet extends GenericServlet {\n\n\tprivate static final long serialVersionUID = 1L;\n\tprivate static final String METHOD_OPTIONS = \"OPTIONS\";\n\n\t@Override\n\tpublic void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {\n\t\tif (!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)) {\n\t\t\tthrow new ServletException(\"non-HTTP request or response\");\n\t\t}\n\t\tservice((HttpServletRequest) req, (HttpServletResponse) res);\n\t}\n\n\tprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n\t\tif (HttpSettings.responseHeaders() != null) {\n\t\t\tfor (Entry<String, String> entry : HttpSettings.responseHeaders().entrySet()) {\n\t\t\t\tresp.setHeader(entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t}\n\t\tString method = req.getMethod().toUpperCase();\n\t\tif (!HttpSettings.allHttpMethods().contains(method)) {\n\t\t\tif (METHOD_OPTIONS.equals(method)) {\n\t\t\t\tthis.doOptions(req, resp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tInnerHttpUtil.sendError(resp, HttpErrorCode.METHOD_UNSUPPORT, \"NOT SUPPORTED\", AppInfo.UTF8);\n\t\t\treturn;\n\t\t}\n\t\tthis.handle(req, resp);\n\t}\n\n\tprotected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n\t\tBiConsumer<HttpServletRequest, HttpServletResponse> h = InnerHttpUtil.getOptionMethodHandler();\n\t\tif (h != null) {\n\t\t\th.accept(req, resp);\n\t\t\treturn;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (String m : HttpSettings.allHttpMethods()) {\n\t\t\tsb.append(m).append(\", \");\n\t\t}\n\t\tsb.append(METHOD_OPTIONS);\n\t\tString allow = sb.toString();\n\t\tresp.setHeader(\"Allow\", allow);\n\t}\n\n\tprotected abstract void handle(HttpServletRequest req, HttpServletResponse resp)\n\t\t\tthrows ServletException, IOException;\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/server/DocumentServlet.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.server;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.http.SumkServlet;\nimport org.yx.common.json.GsonHelper;\nimport org.yx.common.monitor.Monitors;\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.http.act.HttpActions;\nimport org.yx.http.kit.InnerHttpUtil;\nimport org.yx.http.monitor.HttpMonitors;\nimport org.yx.log.Logs;\nimport org.yx.util.StringUtil;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\n\n@Bean\n@SumkServlet(path = { \"/_sumk_acts/*\" }, loadOnStartup = -1, appKey = \"sumkActs\")\npublic class DocumentServlet extends AbstractCommonHttpServlet {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t@Override\n\tprotected void handle(HttpServletRequest req, HttpServletResponse resp) throws IOException {\n\t\tif (!InnerHttpUtil.preServerHandle(req, resp, \"sumk.acts.info\")) {\n\t\t\treturn;\n\t\t}\n\t\tString mode = req.getParameter(\"mode\");\n\t\tif (StringUtil.isEmpty(mode)) {\n\t\t\tLogs.http().debug(\"mode is empty\");\n\t\t\treturn;\n\t\t}\n\t\tGsonBuilder builder = GsonHelper.builder(\"sumk.acts\");\n\t\tif (\"1\".equals(req.getParameter(\"pretty\"))) {\n\t\t\tbuilder.setPrettyPrinting();\n\t\t}\n\t\tGson gson = builder.create();\n\t\tif (mode.equals(\"http\")) {\n\t\t\tList<Map<String, Object>> list = HttpActions.infos(\"1\".equals(req.getParameter(\"full\")));\n\t\t\tlist = this.filter(list, req);\n\t\t\twrite(resp, gson.toJson(list));\n\t\t\treturn;\n\t\t}\n\t\tif (mode.equals(\"rpc\")) {\n\t\t\tString key = \"1\".equals(req.getParameter(\"full\")) ? \"rpc-full\" : \"rpc-simple\";\n\t\t\tObject ret = Monitors.getMessage(\"document\", key, null);\n\t\t\tif (ret == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tList<Map<String, Object>> list = (List<Map<String, Object>>) ret;\n\t\t\tlist = this.filter(list, req);\n\t\t\twrite(resp, gson.toJson(list));\n\t\t\treturn;\n\t\t}\n\t\tif (mode.equals(\"beans.full\")) {\n\t\t\twrite(resp, HttpMonitors.beansName());\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate List<Map<String, Object>> filter(List<Map<String, Object>> list, HttpServletRequest req) {\n\t\tif (list == null || list.isEmpty() || AppInfo.getBoolean(\"sumk.acts.search.disable\", false)) {\n\t\t\treturn list;\n\t\t}\n\t\tSet<String> keys = new HashSet<>();\n\t\tfor (Map<String, Object> map : list) {\n\t\t\tkeys.addAll(map.keySet());\n\t\t}\n\t\tMap<String, String> params = this.getFilterParams(req, keys);\n\t\tif (params.isEmpty()) {\n\t\t\treturn list;\n\t\t}\n\t\tList<Map<String, Object>> ret = new ArrayList<>(list.size());\n\t\tfor (Map<String, Object> map : list) {\n\t\t\tif (contain(map, params)) {\n\t\t\t\tret.add(map);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\tprivate Map<String, String> getFilterParams(HttpServletRequest req, Collection<String> keys) {\n\t\tMap<String, String> map = new HashMap<>();\n\t\tfor (String key : keys) {\n\t\t\tString v = req.getParameter(\"_\" + key);\n\t\t\tif (v != null) {\n\t\t\t\tv = v.toLowerCase().trim();\n\t\t\t}\n\t\t\tif (StringUtil.isNotEmpty(v)) {\n\t\t\t\tmap.put(key, v);\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n\tprivate boolean contain(Map<String, Object> source, Map<String, String> params) {\n\t\tfor (String key : params.keySet()) {\n\t\t\tObject obj = source.get(key);\n\t\t\tif (obj == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tString v = S.json().toJson(obj);\n\t\t\tif (!v.toLowerCase().contains(params.get(key))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void write(HttpServletResponse resp, String msg) throws IOException {\n\t\tif (msg == null) {\n\t\t\treturn;\n\t\t}\n\t\tresp.getOutputStream().write(msg.getBytes(AppInfo.UTF8));\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/server/HttpMethod.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.server;\n\n/**\n * 只有这里定义的方法，才能在@Web中使用\n */\npublic final class HttpMethod {\n\tpublic static final String POST = \"POST\";\n\tpublic static final String GET = \"GET\";\n\tpublic static final String PUT = \"PUT\";\n\tpublic static final String DELETE = \"DELETE\";\n\tpublic static final String PATCH = \"PATCH\";\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/server/MultipartServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.server;\n\nimport javax.servlet.annotation.MultipartConfig;\n\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.http.SumkServlet;\nimport org.yx.common.locale.I18n;\nimport org.yx.exception.BizException;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.handler.HttpHandlerChain;\nimport org.yx.http.handler.MultipartHolder;\nimport org.yx.http.handler.WebContext;\n\n/**\n * \n * @author Administrator\n */\n@Bean\n@SumkServlet(path = { \"/upload/*\" }, loadOnStartup = -1, appKey = \"upload\")\n@MultipartConfig(location = \"\", maxFileSize = -1L, maxRequestSize = -1L, fileSizeThreshold = -1)\npublic class MultipartServer extends AbstractActionServer {\n\n\tprivate static final long serialVersionUID = 1L;\n\tprivate static final String MULTIPART_FORMDATA = \"multipart/form-data\";\n\n\t@Override\n\tprotected void handle(WebContext wc) throws Throwable {\n\t\tif (HttpHandlerChain.multipart == null) {\n\t\t\tlog.error(\"上传功能被禁用\");\n\t\t\tthrow BizException.create(HttpErrorCode.UPLOAD_DISABLED, \"上传功能暂时无法使用\");\n\t\t}\n\t\tString contextType = wc.httpRequest().getContentType();\n\t\tif (contextType == null || !contextType.startsWith(MULTIPART_FORMDATA)) {\n\t\t\tlog.error(\"the MIME of \" + wc.rawAct() + \" act is \" + MULTIPART_FORMDATA + \",not \" + contextType);\n\t\t\tthrow BizException.create(HttpErrorCode.UPLOAD_NOT_MULTI_TYPE,\n\t\t\t\t\tI18n.get(\"sumk.http.upload.error.contentType\", \"该接口的ContentType不是\" + MULTIPART_FORMDATA));\n\t\t}\n\t\ttry {\n\t\t\tHttpHandlerChain.multipart.handle(wc);\n\t\t} finally {\n\t\t\tMultipartHolder.remove();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/server/RestServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.server;\n\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.http.SumkServlet;\nimport org.yx.http.handler.HttpHandlerChain;\nimport org.yx.http.handler.WebContext;\n\n@Bean\n@SumkServlet(path = { \"/rest/*\" }, loadOnStartup = 1, appKey = \"rest\")\npublic class RestServer extends AbstractActionServer {\n\n\tprivate static final long serialVersionUID = 7437235491L;\n\n\t@Override\n\tprotected void handle(WebContext wc) throws Throwable {\n\t\tHttpHandlerChain.rest.handle(wc);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/server/SumkMonitor.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.server;\n\nimport static org.yx.conf.Const.LN;\n\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.TreeMap;\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.http.SumkServlet;\nimport org.yx.base.sumk.UnsafeStringWriter;\nimport org.yx.common.action.ActionStatis;\nimport org.yx.common.action.StatisItem;\nimport org.yx.common.monitor.Monitors;\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.http.act.HttpActionInfo;\nimport org.yx.http.act.HttpActions;\nimport org.yx.http.kit.InnerHttpUtil;\nimport org.yx.http.monitor.HttpMonitors;\nimport org.yx.http.user.AbstractUserSession;\nimport org.yx.http.user.SessionObject;\nimport org.yx.http.user.TimedCachedObject;\nimport org.yx.http.user.UserSession;\nimport org.yx.http.user.WebSessions;\nimport org.yx.util.StringUtil;\nimport org.yx.util.SumkDate;\nimport org.yx.util.SumkThreadPool;\n\n@Bean\n@SumkServlet(path = { \"/_sumk_monitor/*\" }, loadOnStartup = -1, appKey = \"sumkMonitor\")\npublic class SumkMonitor extends AbstractCommonHttpServlet {\n\n\tprivate static final long serialVersionUID = 2364534491L;\n\tprivate static final String TYPE_SPLIT = \"\\n\\n\\n\";\n\n\t@Override\n\tprotected void handle(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n\t\tif (!InnerHttpUtil.preServerHandle(req, resp, \"sumk.http.monitor\")) {\n\t\t\treturn;\n\t\t}\n\n\t\tUnsafeStringWriter writer = new UnsafeStringWriter(512);\n\n\t\tthis.outputServerInfo(req, writer);\n\t\tthis.outputStatis(req, writer);\n\t\tthis.outputNoVisit(req, writer);\n\t\tthis.dbVisitInfo(req, writer);\n\t\tthis.outputBeans(req, writer);\n\t\tthis.outputSystem(req, writer);\n\t\tthis.outputJvmInfo(req, writer);\n\t\tthis.outputGcInfo(req, writer);\n\t\tthis.outputAllTrack(req, writer);\n\t\tthis.outputThreadPool(req, writer);\n\t\tthis.outputLogLevels(req, writer);\n\t\tthis.outputLocalSessions(req, writer);\n\t\tthis.outputAppInfo(req, writer);\n\t\tthis.outputDataSource(req, writer);\n\t\tthis.outputSumkDate(req, writer);\n\t\tthis.outputRpcDatas(req, writer);\n\n\t\twriter.flush();\n\t\tString ret = writer.toString();\n\t\tresp.getOutputStream().write(ret.getBytes(StandardCharsets.UTF_8));\n\t}\n\n\tprivate String localSessions(boolean full) {\n\t\tUserSession userSession = WebSessions.userSession();\n\t\tif (userSession == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(\"##localSessions:\").append(\"  \").append(userSession.localCacheSize());\n\t\tif (full && (userSession instanceof AbstractUserSession)) {\n\t\t\tAbstractUserSession us = (AbstractUserSession) userSession;\n\t\t\tboolean fullKey = AppInfo.getBoolean(\"sumk.http.monitor.session.fullkey\", false);\n\t\t\tfor (Entry<String, TimedCachedObject> en : us.localCache().entrySet()) {\n\t\t\t\tString sessionId = en.getKey();\n\t\t\t\tif (!fullKey) {\n\t\t\t\t\tint mark = sessionId.length() / 2;\n\t\t\t\t\tsessionId = \"**\" + sessionId.substring(mark);\n\t\t\t\t}\n\t\t\t\tsb.append(AppInfo.LN).append(sessionId).append(\" : \")\n\t\t\t\t\t\t.append(SumkDate.of(en.getValue().getRefreshTime())).append(\"   \")\n\t\t\t\t\t\t.append(S.json().fromJson(en.getValue().getJson(), SessionObject.class).getUserId());\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate void outputLocalSessions(HttpServletRequest req, Writer writer) throws IOException {\n\t\tString local = req.getParameter(\"localSessions\");\n\t\tif (\"1\".equals(local)) {\n\t\t\twriter.append(localSessions(false));\n\t\t} else if (\"full\".equals(local)) {\n\t\t\twriter.append(localSessions(true));\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputServerInfo(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"server\"))) {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(HttpMonitors.serverInfo());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputBeans(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"beans\"))) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<String> names = HttpMonitors.beans();\n\t\tStringBuilder sb = new StringBuilder().append(\"##beans:\").append(names.size()).append(Const.LN);\n\t\tfor (String name : names) {\n\t\t\tsb.append(name).append(Const.LN);\n\t\t}\n\t\twriter.append(sb.toString());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputStatis(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"statis\"))) {\n\t\t\treturn;\n\t\t}\n\t\tActionStatis actStatic = InnerHttpUtil.getActionStatis();\n\t\tboolean needReset = \"1\".equals(req.getParameter(\"statis.reset\"))\n\t\t\t\t&& AppInfo.getBoolean(\"sumk.http.statis.reset.allow\", true);\n\t\tMap<String, StatisItem> map = needReset ? actStatic.getAndReset() : actStatic.getAll();\n\t\tList<StatisItem> values = new ArrayList<>(map.values());\n\t\tvalues.sort((a, b) -> Long.compare(b.getSuccessTime(), a.getSuccessTime()));\n\t\tlong totalSuccessCount = 0;\n\t\tlong totalSuccessTime = 0;\n\t\tlong totalFailCount = 0;\n\t\tlong totalFailTime = 0;\n\t\tboolean onlyfailed = \"1\".equals(req.getParameter(\"statis.onlyfailed\"));\n\t\tStringBuilder sb = new StringBuilder(\"##\").append(StatisItem.header()).append(LN);\n\t\tfor (StatisItem v : values) {\n\t\t\tif (onlyfailed && v.getFailedCount() == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsb.append(v.toSimpleString()).append(LN);\n\t\t\ttotalSuccessCount += v.getSuccessCount();\n\t\t\ttotalSuccessTime += v.getSuccessTime();\n\t\t\ttotalFailCount += v.getFailedCount();\n\t\t\ttotalFailTime += v.getFailedTime();\n\t\t}\n\t\tlong c = totalSuccessCount;\n\t\tlong t = totalSuccessTime;\n\t\tdouble avg = c == 0 ? 0 : t * 1d / c;\n\t\tString total = String.join(\"   \", \"**TOTAL**\", String.valueOf(c), String.valueOf(t),\n\t\t\t\tString.valueOf(Math.round(avg)), String.valueOf(totalFailCount), String.valueOf(totalFailTime));\n\t\tsb.append(total).append(LN);\n\t\twriter.append(sb.toString());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputNoVisit(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"noVisit\"))) {\n\t\t\treturn;\n\t\t}\n\t\tCollection<HttpActionInfo> all = HttpActions.actions();\n\t\tMap<String, StatisItem> visited = InnerHttpUtil.getActionStatis().getAll();\n\t\tStringBuilder sb = new StringBuilder(500).append(\"##total(不含login):  \").append(all.size()).append(\"   \")\n\t\t\t\t.append(\"visited(含login):  \").append(visited.size()).append(LN);\n\t\tfor (HttpActionInfo info : all) {\n\t\t\tif (visited.containsKey(info.rawAct())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsb.append(info.rawAct()).append(\"   \").append(info.node().cnName()).append(LN);\n\t\t}\n\n\t\twriter.append(sb.toString());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputSystem(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"system\"))) {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(HttpMonitors.systemInfo());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputJvmInfo(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"jvm\"))) {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(HttpMonitors.jvmInfo());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputAllTrack(HttpServletRequest req, Writer writer) throws IOException {\n\t\tString stack = req.getParameter(\"stack\");\n\t\tif (\"1\".equals(stack)) {\n\t\t\twriter.append(HttpMonitors.stack(false));\n\t\t} else if (\"full\".equals(stack)) {\n\t\t\twriter.append(HttpMonitors.stack(true));\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputThreadPool(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"threadpool\"))) {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(HttpMonitors.threadPoolInfo((ThreadPoolExecutor) SumkThreadPool.executor()));\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputLogLevels(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"logLevel\"))) {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(HttpMonitors.logLevels());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputAppInfo(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"appinfo\")) || !AppInfo.getBoolean(\"sumk.appinfo.monitor\", false)) {\n\t\t\treturn;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tnew TreeMap<>(AppInfo.subMap(\"\")).forEach((k, v) -> {\n\t\t\tsb.append(k).append(\" = \").append(v.replace(LN, \"\\\\n\")).append(LN);\n\t\t});\n\t\twriter.append(sb.toString());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void dbVisitInfo(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"db.cache\"))) {\n\t\t\treturn;\n\t\t}\n\t\tObject ret = Monitors.getMessage(\"monitor\", \"db.cache\", null);\n\t\tif (ret == null) {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(ret.toString());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputDataSource(HttpServletRequest req, Writer writer) throws IOException {\n\t\tString ds = req.getParameter(\"datasource\");\n\t\tif (StringUtil.isEmpty(ds)) {\n\t\t\treturn;\n\t\t}\n\t\tObject ret = Monitors.getMessage(\"monitor\", \"datasource\", ds);\n\t\tif (ret == null) {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(ret.toString());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputSumkDate(HttpServletRequest req, UnsafeStringWriter writer) {\n\t\tif (!\"1\".equals(req.getParameter(\"sumkdate\"))) {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(HttpMonitors.sumkDateCacheChangeCount());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputGcInfo(HttpServletRequest req, UnsafeStringWriter writer) {\n\t\tif (!\"1\".equals(req.getParameter(\"gc\"))) {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(HttpMonitors.gcInfo());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n\n\tprivate void outputRpcDatas(HttpServletRequest req, Writer writer) throws IOException {\n\t\tif (!\"1\".equals(req.getParameter(\"rpcData\"))) {\n\t\t\treturn;\n\t\t}\n\t\tObject ret = Monitors.getMessage(\"monitor\", \"rpcData\", null);\n\t\tif (ret == null) {\n\t\t\treturn;\n\t\t}\n\t\twriter.append(ret.toString());\n\t\twriter.append(TYPE_SPLIT);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/spec/HttpSpecs.java",
    "content": "package org.yx.http.spec;\n\nimport java.lang.reflect.Method;\nimport java.util.Objects;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\nimport org.yx.annotation.http.SumkFilter;\nimport org.yx.annotation.http.SumkServlet;\nimport org.yx.annotation.http.Upload;\nimport org.yx.annotation.http.Web;\nimport org.yx.annotation.spec.Specs;\n\npublic class HttpSpecs extends Specs {\n\n\tprivate static BiFunction<Object, Method, WebSpec> webParser = (bean, m) -> {\n\t\tWeb web = m.getAnnotation(Web.class);\n\t\tif (web == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new WebSpec(web.value(), web.cnName(), web.requireLogin(), web.requestType(), web.sign(),\n\t\t\t\tweb.responseType(), web.tags(), web.toplimit(), web.method());\n\t};\n\n\tprivate static BiFunction<Object, Method, UploadSpec> uploadParser = (bean, m) -> {\n\t\tUpload c = m.getAnnotation(Upload.class);\n\t\tif (c == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new UploadSpec(c.paramName());\n\t};\n\n\tprivate static Function<Class<?>, SumkServletSpec> sumkServletParser = clz -> {\n\t\tSumkServlet f = clz.getAnnotation(SumkServlet.class);\n\t\tif (f == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new SumkServletSpec(f.name(), f.path(), f.loadOnStartup(), f.asyncSupported(), f.appKey());\n\t};\n\n\tprivate static Function<Class<?>, SumkFilterSpec> sumkFilterParser = clz -> {\n\t\tSumkFilter f = clz.getAnnotation(SumkFilter.class);\n\t\tif (f == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new SumkFilterSpec(f.name(), f.path(), f.dispatcherType(), f.isMatchAfter(), f.asyncSupported());\n\t};\n\n\tpublic static Function<Class<?>, SumkFilterSpec> getSumkFilterParser() {\n\t\treturn sumkFilterParser;\n\t}\n\n\tpublic static Function<Class<?>, SumkServletSpec> getSumkServletParser() {\n\t\treturn sumkServletParser;\n\t}\n\n\tpublic static BiFunction<Object, Method, UploadSpec> getUploadParser() {\n\t\treturn uploadParser;\n\t}\n\n\tpublic static BiFunction<Object, Method, WebSpec> getWebParser() {\n\t\treturn webParser;\n\t}\n\n\tpublic static void setSumkFilterParser(Function<Class<?>, SumkFilterSpec> sumkFilterParser) {\n\t\tHttpSpecs.sumkFilterParser = Objects.requireNonNull(sumkFilterParser);\n\t}\n\n\tpublic static void setSumkServletParser(Function<Class<?>, SumkServletSpec> sumkServletParser) {\n\t\tHttpSpecs.sumkServletParser = Objects.requireNonNull(sumkServletParser);\n\t}\n\n\tpublic static void setUploadParser(BiFunction<Object, Method, UploadSpec> uploadParser) {\n\t\tHttpSpecs.uploadParser = Objects.requireNonNull(uploadParser);\n\t}\n\n\tpublic static void setWebParser(BiFunction<Object, Method, WebSpec> webParser) {\n\t\tHttpSpecs.webParser = Objects.requireNonNull(webParser);\n\t}\n\n\tpublic static WebSpec extractWeb(Object bean, Method m) {\n\t\treturn parse(bean, m, webParser);\n\t}\n\n\tpublic static UploadSpec extractUpload(Object bean, Method m) {\n\t\treturn parse(bean, m, uploadParser);\n\t}\n\n\tpublic static SumkServletSpec extractSumkServlet(Class<?> clz) {\n\t\treturn parse(clz, sumkServletParser);\n\t}\n\n\tpublic static SumkFilterSpec extractSumkFilter(Class<?> clz) {\n\t\treturn parse(clz, sumkFilterParser);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/spec/SumkFilterSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.spec;\n\nimport javax.servlet.DispatcherType;\n\npublic class SumkFilterSpec {\n\tprivate final String name;\n\tprivate final String[] path;\n\tprivate final DispatcherType[] dispatcherType;\n\tprivate final boolean isMatchAfter;\n\tprivate final boolean asyncSupported;\n\n\tpublic SumkFilterSpec(String name, String[] path, DispatcherType[] dispatcherType, boolean isMatchAfter,\n\t\t\tboolean asyncSupported) {\n\t\tthis.name = name;\n\t\tthis.path = path;\n\t\tthis.dispatcherType = dispatcherType;\n\t\tthis.isMatchAfter = isMatchAfter;\n\t\tthis.asyncSupported = asyncSupported;\n\t}\n\n\tpublic String name() {\n\t\treturn this.name;\n\t}\n\n\tpublic String[] path() {\n\t\treturn this.path;\n\t}\n\n\tpublic boolean asyncSupported() {\n\t\treturn this.asyncSupported;\n\t}\n\n\tpublic DispatcherType[] dispatcherType() {\n\t\treturn dispatcherType;\n\t}\n\n\tpublic boolean isMatchAfter() {\n\t\treturn isMatchAfter;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/spec/SumkServletSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.spec;\n\nimport java.util.Objects;\n\npublic class SumkServletSpec {\n\tprivate final String name;\n\tprivate final String[] path;\n\tprivate final int loadOnStartup;\n\tprivate final boolean asyncSupported;\n\tprivate final String appKey;\n\n\tpublic SumkServletSpec(String name, String[] path, int loadOnStartup, boolean asyncSupported, String appKey) {\n\t\tthis.name = name;\n\t\tthis.path = Objects.requireNonNull(path);\n\t\tthis.loadOnStartup = loadOnStartup;\n\t\tthis.asyncSupported = asyncSupported;\n\t\tthis.appKey = appKey;\n\t}\n\n\tpublic String name() {\n\t\treturn this.name;\n\t}\n\n\tpublic String[] path() {\n\t\treturn this.path;\n\t}\n\n\tpublic int loadOnStartup() {\n\t\treturn this.loadOnStartup;\n\t}\n\n\tpublic boolean asyncSupported() {\n\t\treturn this.asyncSupported;\n\t}\n\n\tpublic String appKey() {\n\t\treturn this.appKey;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/spec/UploadSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.spec;\n\nimport java.util.Objects;\n\npublic class UploadSpec {\n\tprivate final String paramName;\n\n\tpublic UploadSpec(String paramName) {\n\t\tthis.paramName = Objects.requireNonNull(paramName);\n\t}\n\n\tpublic String paramName() {\n\t\treturn this.paramName;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/spec/WebSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.spec;\n\nimport java.util.Objects;\n\nimport org.yx.http.MessageType;\n\npublic class WebSpec {\n\n\tprivate final String value;\n\tprivate final String cnName;\n\tprivate final boolean requireLogin;\n\tprivate final MessageType requestType;\n\tprivate final boolean sign;\n\tprivate final MessageType responseType;\n\tprivate final String[] tags;\n\tprivate final int toplimit;\n\tprivate final String[] method;\n\n\tpublic WebSpec(String value, String cnName, boolean requireLogin, MessageType requestType, boolean sign,\n\t\t\tMessageType responseType, String[] tags, int toplimit, String[] method) {\n\t\tthis.value = Objects.requireNonNull(value);\n\t\tthis.cnName = Objects.requireNonNull(cnName);\n\t\tthis.requireLogin = requireLogin;\n\t\tthis.requestType = Objects.requireNonNull(requestType);\n\t\tthis.sign = sign;\n\t\tthis.responseType = Objects.requireNonNull(responseType);\n\t\tthis.tags = Objects.requireNonNull(tags);\n\t\tthis.toplimit = toplimit;\n\t\tthis.method = Objects.requireNonNull(method);\n\t}\n\n\tpublic String value() {\n\t\treturn this.value;\n\t}\n\n\tpublic String cnName() {\n\t\treturn this.cnName;\n\t}\n\n\tpublic boolean requireLogin() {\n\t\treturn this.requireLogin;\n\t}\n\n\tpublic MessageType requestType() {\n\t\treturn this.requestType;\n\t}\n\n\tpublic boolean sign() {\n\t\treturn this.sign;\n\t}\n\n\tpublic MessageType responseType() {\n\t\treturn this.responseType;\n\t}\n\n\tpublic String[] tags() {\n\t\treturn this.tags;\n\t}\n\n\tpublic int toplimit() {\n\t\treturn this.toplimit;\n\t}\n\n\tpublic String[] method() {\n\t\treturn this.method;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/start/JettyHandlerSupplier.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.start;\n\nimport java.util.Objects;\nimport java.util.function.Supplier;\n\nimport org.eclipse.jetty.server.handler.ResourceHandler;\nimport org.eclipse.jetty.server.handler.gzip.GzipHandler;\nimport org.eclipse.jetty.server.session.SessionHandler;\nimport org.yx.conf.AppInfo;\n\npublic class JettyHandlerSupplier {\n\tprivate static Supplier<GzipHandler> gzipHandlerSupplier = () -> {\n\t\tGzipHandler h = new GzipHandler();\n\t\th.addIncludedMethods(\"POST\");\n\t\th.setMinGzipSize(AppInfo.getInt(\"sumk.webserver.gzip.minsize\", 1000));\n\t\treturn h;\n\t};\n\n\tprivate static Supplier<ResourceHandler> resourceHandlerSupplier = () -> {\n\t\tResourceHandler handler = new ResourceHandler();\n\t\tString welcomes = AppInfo.get(\"sumk.webserver.resource.welcomes\");\n\t\tif (welcomes != null && welcomes.length() > 0) {\n\t\t\thandler.setWelcomeFiles(welcomes.replace('，', ',').split(\",\"));\n\t\t}\n\t\treturn handler;\n\t};\n\n\tprivate static Supplier<SessionHandler> sessionHandlerSupplier = SessionHandler::new;\n\n\tpublic static Supplier<GzipHandler> gzipHandlerSupplier() {\n\t\treturn gzipHandlerSupplier;\n\t}\n\n\tpublic static void setGzipHandlerSupplier(Supplier<GzipHandler> h) {\n\t\tJettyHandlerSupplier.gzipHandlerSupplier = Objects.requireNonNull(h);\n\t}\n\n\tpublic static Supplier<ResourceHandler> resourceHandlerSupplier() {\n\t\treturn resourceHandlerSupplier;\n\t}\n\n\tpublic static void setResourceHandlerSupplier(Supplier<ResourceHandler> h) {\n\t\tJettyHandlerSupplier.resourceHandlerSupplier = Objects.requireNonNull(h);\n\t}\n\n\tpublic static Supplier<SessionHandler> sessionHandlerSupplier() {\n\t\treturn sessionHandlerSupplier;\n\t}\n\n\tpublic static void setSessionHandlerSupplier(Supplier<SessionHandler> h) {\n\t\tJettyHandlerSupplier.sessionHandlerSupplier = Objects.requireNonNull(h);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/start/JettyHttpsServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.start;\n\nimport java.io.File;\nimport java.net.URISyntaxException;\n\nimport org.eclipse.jetty.server.ConnectionFactory;\nimport org.eclipse.jetty.server.HttpConnectionFactory;\nimport org.eclipse.jetty.server.ServerConnector;\nimport org.eclipse.jetty.server.SslConnectionFactory;\nimport org.eclipse.jetty.util.ssl.SslContextFactory;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.util.FileUtil;\n\npublic class JettyHttpsServer extends JettyServer {\n\n\tpublic JettyHttpsServer(int port) {\n\t\tsuper(port);\n\t}\n\n\t@Override\n\tprotected ConnectionFactory[] getConnectionFactorys() throws URISyntaxException {\n\t\t@SuppressWarnings(\"deprecation\")\n\t\tSslContextFactory sslContextFactory = new SslContextFactory();\n\t\tString path = get(Const.KEY_STORE_PATH);\n\t\tFile keystoreFile = FileUtil.file(path);\n\t\tif (!keystoreFile.exists()) {\n\t\t\tString msg = path + \" is not exist\";\n\t\t\tLogs.http().error(msg);\n\t\t\tthrow new SumkException(2345345, msg);\n\t\t}\n\t\tsslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());\n\t\tsslContextFactory.setKeyStorePassword(get(\"sumk.webserver.ssl.storePassword\"));\n\t\tsslContextFactory.setKeyManagerPassword(get(\"sumk.webserver.ssl.managerPassword\"));\n\t\tsslContextFactory.setCertAlias(get(\"sumk.webserver.ssl.alias\"));\n\n\t\tString v = AppInfo.get(\"sumk.webserver.ssl.storeType\", null);\n\t\tif (v != null) {\n\t\t\tsslContextFactory.setKeyStoreType(v);\n\t\t}\n\n\t\tsslContextFactory.setTrustAll(AppInfo.getBoolean(\"sumk.webserver.ssl.trustAll\", false));\n\n\t\tLogs.http().info(\"using https\");\n\t\treturn new ConnectionFactory[] {\n\t\t\t\tnew SslConnectionFactory(sslContextFactory, AppInfo.get(\"sumk.webserver.ssl.protocol\", \"http/1.1\")),\n\t\t\t\tnew HttpConnectionFactory() };\n\t}\n\n\tprivate String get(String name) {\n\t\tString v = AppInfo.get(name, null);\n\t\tif (v == null) {\n\t\t\tthrow new SumkException(12391763, name + \" is null!!! please set it\");\n\t\t}\n\t\treturn v;\n\t}\n\n\t@Override\n\tprotected ServerConnector createConnector() throws Exception {\n\t\tServerConnector connector = super.createConnector();\n\t\tconnector.setDefaultProtocol(AppInfo.get(\"sumk.webserver.ssl.protocol\", \"SSL\"));\n\t\treturn connector;\n\t}\n\n}"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/start/JettyServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.start;\n\nimport java.util.Arrays;\nimport java.util.EventListener;\nimport java.util.List;\n\nimport javax.servlet.ServletContextListener;\n\nimport org.eclipse.jetty.server.ConnectionFactory;\nimport org.eclipse.jetty.server.Connector;\nimport org.eclipse.jetty.server.HttpConnectionFactory;\nimport org.eclipse.jetty.server.Server;\nimport org.eclipse.jetty.server.ServerConnector;\nimport org.eclipse.jetty.server.handler.ContextHandler.ContextScopeListener;\nimport org.eclipse.jetty.server.handler.HandlerWrapper;\nimport org.eclipse.jetty.server.handler.ResourceHandler;\nimport org.eclipse.jetty.server.handler.gzip.GzipHandler;\nimport org.eclipse.jetty.server.session.SessionHandler;\nimport org.eclipse.jetty.servlet.ServletContextHandler;\nimport org.eclipse.jetty.util.thread.ExecutorThreadPool;\nimport org.yx.base.Lifecycle;\nimport org.yx.base.context.AppContext;\nimport org.yx.bean.IOC;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.main.SumkServer;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic class JettyServer implements Lifecycle {\n\n\tprotected final int port;\n\tprotected Server server;\n\tprivate boolean started = false;\n\n\tpublic JettyServer(int port) {\n\t\tthis.port = port;\n\t\tthis.init();\n\t}\n\n\tprotected ConnectionFactory[] getConnectionFactorys() throws Exception {\n\t\treturn new ConnectionFactory[] { new HttpConnectionFactory() };\n\t}\n\n\tprotected ServerConnector createConnector() throws Exception {\n\n\t\tServerConnector connector = new JettyServerConnector(server, null, null, null,\n\t\t\t\tAppInfo.getInt(\"sumk.webserver.connector.acceptors\", -1),\n\t\t\t\tAppInfo.getInt(\"sumk.webserver.connector.selectors\", -1), getConnectionFactorys());\n\t\tconnector.setReuseAddress(AppInfo.getBoolean(\"sumk.webserver.reuseAddr\", false));\n\t\tint backlog = AppInfo.getInt(\"sumk.webserver.backlog\", 0);\n\t\tif (backlog > 0) {\n\t\t\tconnector.setAcceptQueueSize(backlog);\n\t\t}\n\t\treturn connector;\n\t}\n\n\tprotected synchronized void init() {\n\t\ttry {\n\t\t\tbuildJettyProperties();\n\t\t\tserver = new Server(new ExecutorThreadPool(SumkServer.getHttpExecutor()));\n\t\t\tServerConnector connector = this.createConnector();\n\t\t\tLogs.http().info(\"http listen at port: {}\", port);\n\t\t\tString host = SumkServer.httpHost();\n\t\t\tif (host != null && host.length() > 0) {\n\t\t\t\tconnector.setHost(host);\n\t\t\t}\n\t\t\tconnector.setPort(port);\n\n\t\t\tserver.setConnectors(new Connector[] { connector });\n\t\t\tServletContextHandler context = createServletContextHandler();\n\t\t\tcontext.setContextPath(AppInfo.get(\"sumk.webserver.root\", \"sumk.jetty.web.root\", \"/\"));\n\t\t\tcontext.addEventListener(new SumkLoaderListener());\n\t\t\taddUserListener(context, Arrays.asList(ServletContextListener.class, ContextScopeListener.class));\n\t\t\tString resourcePath = AppInfo.get(\"sumk.webserver.resource\");\n\t\t\tif (StringUtil.isNotEmpty(resourcePath)) {\n\t\t\t\tResourceHandler resourceHandler = JettyHandlerSupplier.resourceHandlerSupplier().get();\n\t\t\t\tif (resourceHandler != null) {\n\t\t\t\t\tresourceHandler.setResourceBase(resourcePath);\n\t\t\t\t\tcontext.insertHandler(resourceHandler);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (AppInfo.getBoolean(\"sumk.webserver.session.enable\", false)) {\n\t\t\t\tSessionHandler h = JettyHandlerSupplier.sessionHandlerSupplier().get();\n\t\t\t\tif (h != null) {\n\t\t\t\t\tcontext.insertHandler(h);\n\t\t\t\t}\n\t\t\t}\n\t\t\taddUserHandlers(context);\n\t\t\tserver.setHandler(context);\n\t\t} catch (Throwable e) {\n\t\t\tLogs.http().error(e.getLocalizedMessage(), e);\n\t\t\tAppContext.startFailed();\n\t\t}\n\n\t}\n\n\tprotected void addUserHandlers(ServletContextHandler context) {\n\t\tObject obj = AppContext.inst().get(HandlerWrapper.class);\n\t\tif (obj instanceof HandlerWrapper[]) {\n\t\t\tHandlerWrapper[] hs = (HandlerWrapper[]) obj;\n\t\t\tfor (HandlerWrapper h : hs) {\n\t\t\t\tLogs.http().info(\"add jetty handler {}\", h);\n\t\t\t\tcontext.insertHandler(h);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void buildJettyProperties() {\n\t\tString key = \"org.eclipse.jetty.server.Request.maxFormContentSize\";\n\t\tString v = AppInfo.get(key);\n\t\tif (v != null && v.length() > 0 && System.getProperty(key) == null) {\n\t\t\tSystem.setProperty(key, v);\n\t\t}\n\t}\n\n\t@Override\n\tpublic synchronized void start() {\n\t\tif (started || server == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tserver.start();\n\t\t\tstarted = true;\n\t\t} catch (Throwable e) {\n\t\t\tLogs.http().error(e.getLocalizedMessage(), e);\n\t\t\tthrow new SumkException(234234, \"jetty启动失败\");\n\t\t}\n\n\t}\n\n\t/**\n\t * @return\n\t */\n\tprivate ServletContextHandler createServletContextHandler() {\n\t\tServletContextHandler handler = IOC.get(ServletContextHandler.class);\n\t\tif (handler != null) {\n\t\t\treturn handler;\n\t\t}\n\t\thandler = new ServletContextHandler();\n\t\tString welcomes = AppInfo.get(\"sumk.webserver.welcomes\");\n\t\tif (welcomes != null && welcomes.length() > 0) {\n\t\t\thandler.setWelcomeFiles(welcomes.replace('，', ',').split(\",\"));\n\t\t}\n\t\tGzipHandler gzipHandler = JettyHandlerSupplier.gzipHandlerSupplier().get();\n\t\tif (gzipHandler != null) {\n\t\t\thandler.setGzipHandler(gzipHandler);\n\t\t}\n\t\treturn handler;\n\t}\n\n\tprivate void addUserListener(ServletContextHandler context, List<Class<? extends EventListener>> intfs) {\n\t\tfor (Class<? extends EventListener> intf : intfs) {\n\t\t\tList<? extends EventListener> listeners = IOC.getBeans(intf);\n\t\t\tif (CollectionUtil.isEmpty(listeners)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (EventListener lis : listeners) {\n\t\t\t\tcontext.addEventListener(lis);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tif (server != null) {\n\t\t\ttry {\n\t\t\t\tserver.stop();\n\t\t\t\tthis.started = false;\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.printStack(\"sumk.http\", e);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/start/JettyServerConnector.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.start;\n\nimport java.io.IOException;\nimport java.nio.channels.ServerSocketChannel;\nimport java.util.concurrent.Executor;\n\nimport org.eclipse.jetty.io.ByteBufferPool;\nimport org.eclipse.jetty.server.ConnectionFactory;\nimport org.eclipse.jetty.server.Server;\nimport org.eclipse.jetty.server.ServerConnector;\nimport org.eclipse.jetty.util.thread.Scheduler;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\n\npublic class JettyServerConnector extends ServerConnector {\n\n\tpublic JettyServerConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool,\n\t\t\tint acceptors, int selectors, ConnectionFactory[] factories) {\n\t\tsuper(server, executor, scheduler, bufferPool, acceptors, selectors, factories);\n\t}\n\n\t@Override\n\tprotected ServerSocketChannel openAcceptChannel() throws IOException {\n\t\tIOException ex = null;\n\t\ttry {\n\t\t\treturn super.openAcceptChannel();\n\t\t} catch (IOException e) {\n\t\t\tex = e;\n\t\t}\n\n\t\tfor (int i = 0; i < AppInfo.getInt(\"sumk.webserver.bind.retry\", 100); i++) {\n\t\t\ttry {\n\t\t\t\tThread.sleep(AppInfo.getLong(\"sumk.webserver.bind.sleepTime\", 2000));\n\t\t\t} catch (InterruptedException e1) {\n\t\t\t\tLogs.http().error(\"showdown because of InterruptedException\");\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\tthrow new SumkException(34534560, \"收到中断.\" + e1);\n\t\t\t}\n\t\t\tLogs.http().warn(\"{} was occupied({}),begin retry {}\", this.getPort(), ex.getMessage(), i);\n\t\t\ttry {\n\t\t\t\treturn super.openAcceptChannel();\n\t\t\t} catch (IOException e) {\n\t\t\t\tif (isInheritChannel()) {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t\tex = e;\n\t\t\t}\n\t\t}\n\t\tthrow ex;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/start/SumkLoaderListener.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.start;\n\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.EventListener;\nimport java.util.List;\n\nimport javax.servlet.DispatcherType;\nimport javax.servlet.Filter;\nimport javax.servlet.FilterRegistration;\nimport javax.servlet.MultipartConfigElement;\nimport javax.servlet.Servlet;\nimport javax.servlet.ServletContext;\nimport javax.servlet.ServletContextEvent;\nimport javax.servlet.ServletContextListener;\nimport javax.servlet.ServletRegistration;\nimport javax.servlet.annotation.MultipartConfig;\n\nimport org.yx.bean.BeanKit;\nimport org.yx.bean.IOC;\nimport org.yx.conf.AppInfo;\nimport org.yx.http.kit.HttpSettings;\nimport org.yx.http.spec.HttpSpecs;\nimport org.yx.http.spec.SumkFilterSpec;\nimport org.yx.http.spec.SumkServletSpec;\nimport org.yx.http.user.HttpLoginWrapper;\nimport org.yx.http.user.LoginServlet;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.main.StartConstants;\nimport org.yx.main.SumkServer;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\n/**\n * 如果使用tomcat等外部容器启动sumk，需要将sumk.webserver.disable设为1，并在web.xml中添加：<BR>\n * &lt;listener&gt;<br>\n * &nbsp;&nbsp;\n * &lt;listener-class&gt;org.yx.main.SumkLoaderListener&lt;/listener-class&gt;\n * <br>\n * &lt;/listener&gt;\n * \n * @author 游夏\n *\n */\npublic class SumkLoaderListener implements ServletContextListener {\n\n\t@Override\n\tpublic void contextInitialized(ServletContextEvent sce) {\n\t\tLogs.http().debug(\"contextInitialized\");\n\t\ttry {\n\t\t\tSumkServer.start(null, Collections.singleton(StartConstants.EMBED_WEBSERVER_DISABLE));\n\t\t\tif (!SumkServer.isHttpEnable()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tServletContext context = sce.getServletContext();\n\t\t\taddFilters(context);\n\t\t\taddServlets(context);\n\n\t\t\tString path = AppInfo.get(\"sumk.http.login.path\", \"/login/*\");\n\t\t\tif (IOC.getBeans(LoginServlet.class).size() > 0) {\n\t\t\t\tString loginPath = path;\n\t\t\t\tif (!loginPath.startsWith(\"/\")) {\n\t\t\t\t\tloginPath = \"/\" + loginPath;\n\t\t\t\t}\n\t\t\t\tLogs.http().info(\"login path:{}\", context.getContextPath() + loginPath);\n\t\t\t\tServletRegistration.Dynamic dynamic = context.addServlet(loginPath, HttpLoginWrapper.class);\n\t\t\t\tdynamic.addMapping(loginPath);\n\t\t\t\tdynamic.setLoadOnStartup(1);\n\t\t\t}\n\t\t\taddListeners(context);\n\t\t} catch (Exception e) {\n\t\t\tLogs.http().error(e.getMessage(), e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\tprivate void addServlets(ServletContext context) {\n\t\tList<Servlet> servlets = IOC.getBeans(Servlet.class);\n\t\tfor (Servlet bean : servlets) {\n\t\t\tClass<?> targetClz = BeanKit.getTargetClass(bean);\n\t\t\tSumkServletSpec sumk = HttpSpecs.extractSumkServlet(targetClz);\n\t\t\tif (sumk == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString name = sumk.name();\n\t\t\tif (name == null || name.isEmpty()) {\n\t\t\t\tname = bean.getClass().getSimpleName();\n\t\t\t}\n\t\t\tServletRegistration.Dynamic dynamic = context.addServlet(name, bean);\n\t\t\tdynamic.setAsyncSupported(sumk.asyncSupported());\n\t\t\tdynamic.setLoadOnStartup(sumk.loadOnStartup());\n\t\t\tString[] values = sumk.path();\n\t\t\tString value = null;\n\t\t\tif (StringUtil.isNotEmpty(sumk.appKey())\n\t\t\t\t\t&& (value = AppInfo.get(\"sumk.http.servlet.\" + sumk.appKey())) != null) {\n\t\t\t\tvalues = StringUtil.toLatin(value).split(\",\");\n\t\t\t}\n\t\t\tdynamic.addMapping(values);\n\n\t\t\tif (HttpSettings.isUploadEnable()) {\n\t\t\t\tthis.handleUpload(dynamic, targetClz, values);\n\t\t\t}\n\t\t\tLogs.http().trace(\"add web mapping : {} - {} -{}\", name, bean.getClass().getSimpleName(),\n\t\t\t\t\tArrays.toString(values));\n\t\t}\n\t}\n\n\tprivate void handleUpload(ServletRegistration.Dynamic dynamic, Class<?> targetClz, String[] paths) {\n\t\ttry {\n\t\t\tMultipartConfig mc = targetClz.getAnnotation(MultipartConfig.class);\n\t\t\tif (mc == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString location = mc.location();\n\t\t\tif (StringUtil.isEmpty(location)) {\n\t\t\t\tlocation = AppInfo.get(\"sumk.http.multipart.location\", null);\n\t\t\t\tif (location == null) {\n\t\t\t\t\tPath p = Files.createTempDirectory(\"multi\");\n\t\t\t\t\tlocation = p.toFile().getAbsolutePath();\n\t\t\t\t}\n\t\t\t}\n\t\t\tlong maxFileSize = mc.maxFileSize() > 0 ? mc.maxFileSize()\n\t\t\t\t\t: AppInfo.getLong(\"sumk.http.multipart.maxFileSize\", 1024L * 1024 * 10);\n\t\t\tlong maxRequestSize = mc.maxRequestSize() > 0 ? mc.maxRequestSize()\n\t\t\t\t\t: AppInfo.getLong(\"sumk.http.multipart.maxRequestSize\", 1024L * 1024 * 50);\n\n\t\t\tint fileSizeThreshold = mc.fileSizeThreshold() > 0 ? mc.fileSizeThreshold()\n\t\t\t\t\t: AppInfo.getInt(\"sumk.http.multipart.fileSizeThreshold\", 1024 * 10);\n\t\t\tMultipartConfigElement c = new MultipartConfigElement(location, maxFileSize, maxRequestSize,\n\t\t\t\t\tfileSizeThreshold);\n\t\t\tdynamic.setMultipartConfig(c);\n\t\t\tif (Logs.http().isInfoEnabled()) {\n\t\t\t\tLogs.http().info(\"{} location={},maxFileSize={},maxRequestSize={},fileSizeThreshold={}\",\n\t\t\t\t\t\tArrays.toString(paths), location, maxFileSize, maxRequestSize, fileSizeThreshold);\n\t\t\t}\n\t\t} catch (Throwable e) {\n\t\t\tLogs.http().warn(\"不支持文件上传!!!\", e);\n\t\t}\n\t}\n\n\tprivate void addFilters(ServletContext context) {\n\t\tList<Filter> filters = IOC.getBeans(Filter.class);\n\t\tif (org.yx.util.CollectionUtil.isEmpty(filters)) {\n\t\t\treturn;\n\t\t}\n\t\tfor (Filter bean : filters) {\n\t\t\tClass<?> targetClz = BeanKit.getTargetClass(bean);\n\t\t\tSumkFilterSpec sumk = HttpSpecs.extractSumkFilter(targetClz);\n\t\t\tif (sumk == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString name = sumk.name();\n\t\t\tif (name.isEmpty()) {\n\t\t\t\tname = bean.getClass().getSimpleName();\n\t\t\t}\n\t\t\tLogs.http().trace(\"add web filter : {} - {}\", name, bean.getClass().getSimpleName());\n\t\t\tFilterRegistration.Dynamic r = context.addFilter(name, bean);\n\t\t\tr.setAsyncSupported(sumk.asyncSupported());\n\t\t\tDispatcherType[] type = sumk.dispatcherType();\n\t\t\tEnumSet<DispatcherType> types = null;\n\t\t\tif (type.length > 0) {\n\t\t\t\ttypes = EnumSet.copyOf(Arrays.asList(type));\n\t\t\t}\n\n\t\t\tr.addMappingForUrlPatterns(types, sumk.isMatchAfter(), sumk.path());\n\t\t}\n\t}\n\n\tprivate static InputStream openResourceAsStream(String name) {\n\t\tInputStream in = Loader.getResourceAsStream(name + \"-impl\");\n\t\tif (in != null) {\n\t\t\treturn in;\n\t\t}\n\t\treturn Loader.getResourceAsStream(name);\n\t}\n\n\tprivate void addListeners(ServletContext context) {\n\t\ttry {\n\t\t\tInputStream in = openResourceAsStream(\"META-INF/http/listeners\");\n\t\t\taddListener(context, CollectionUtil.loadList(in));\n\t\t} catch (Exception e) {\n\t\t\tLog.printStack(\"sumk.error\", e);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate void addListener(ServletContext context, List<String> intfs) throws ClassNotFoundException {\n\t\tfor (String intf : intfs) {\n\t\t\tClass<?> clz = Loader.loadClass(intf);\n\t\t\tif (!EventListener.class.isAssignableFrom(clz)) {\n\t\t\t\tLogs.http().info(intf + \" is not implement EventListener\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tList<EventListener> listeners = (List<EventListener>) IOC.getBeans(clz);\n\t\t\tif (org.yx.util.CollectionUtil.isEmpty(listeners)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (EventListener lis : listeners) {\n\t\t\t\tLogs.http().trace(\"add web listener:{}\", lis.getClass().getSimpleName());\n\n\t\t\t\tcontext.addListener(lis);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void contextDestroyed(ServletContextEvent sce) {\n\t\tSumkServer.destroy();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/start/WebAnnotationResolver.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.start;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.base.matcher.Matchers;\nimport org.yx.bean.BeanKit;\nimport org.yx.bean.aop.asm.AsmUtils;\nimport org.yx.bean.aop.asm.MethodParamInfo;\nimport org.yx.bean.aop.asm.ParamPojos;\nimport org.yx.common.StringEntity;\nimport org.yx.conf.AppInfo;\nimport org.yx.http.act.HttpActionNode;\nimport org.yx.http.spec.HttpSpecs;\nimport org.yx.http.spec.WebSpec;\nimport org.yx.log.Logs;\nimport org.yx.util.StringUtil;\n\npublic class WebAnnotationResolver {\n\n\tprivate Predicate<String> exclude = BooleanMatcher.FALSE;\n\n\tprivate List<String> rawNames(Method m, WebSpec web) {\n\t\tString name = web.value();\n\t\tif (name == null || name.isEmpty()) {\n\t\t\treturn Collections.singletonList(m.getName());\n\t\t}\n\t\tList<String> list = new ArrayList<String>(1);\n\t\tString[] names = StringUtil.toLatin(name).split(\",\");\n\t\tfor (String raw : names) {\n\t\t\tif (raw != null && (raw = raw.trim()).length() > 0) {\n\t\t\t\tlist.add(raw);\n\t\t\t}\n\t\t}\n\t\treturn list.size() > 0 ? list : Collections.singletonList(m.getName());\n\t}\n\n\tpublic WebAnnotationResolver() {\n\t\tString patterns = AppInfo.get(\"sumk.http.exclude\", null);\n\t\tif (patterns != null) {\n\t\t\tthis.exclude = Matchers.createWildcardMatcher(patterns, 1);\n\t\t\tLogs.http().debug(\"web exclude:{}\", this.exclude);\n\t\t}\n\t}\n\n\tpublic List<StringEntity<HttpActionNode>> resolve(Object bean) throws Exception {\n\n\t\tClass<?> clz = BeanKit.getTargetClass(bean);\n\t\tif (exclude.test(clz.getName())) {\n\t\t\treturn null;\n\t\t}\n\t\tMethod[] methods = clz.getMethods();\n\t\tList<Method> httpMethods = new ArrayList<>();\n\t\tfor (final Method m : methods) {\n\t\t\tif (HttpSpecs.extractWeb(bean, m) == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (AsmUtils.notPublicOnly(m.getModifiers())) {\n\t\t\t\tLogs.http().warn(\"$$$ {}.{} has bad modifiers, maybe static or private\", clz.getName(), m.getName());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\thttpMethods.add(m);\n\t\t}\n\t\tif (httpMethods.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<MethodParamInfo> mpInfos = AsmUtils.buildMethodInfos(httpMethods);\n\t\tList<StringEntity<HttpActionNode>> infos = new ArrayList<>(mpInfos.size() * 2);\n\t\tfor (MethodParamInfo info : mpInfos) {\n\t\t\tMethod m = info.getMethod();\n\t\t\tWebSpec act = HttpSpecs.extractWeb(bean, m);\n\t\t\tHttpActionNode node = new HttpActionNode(bean, m, ParamPojos.create(info), act);\n\n\t\t\tList<String> names = rawNames(m, act);\n\t\t\tif (names == null || names.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (String name : names) {\n\t\t\t\tinfos.add(StringEntity.create(name, node));\n\t\t\t}\n\t\t}\n\t\treturn infos;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/AbstractLoginServlet.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.charset.Charset;\n\nimport javax.servlet.ServletConfig;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.slf4j.Logger;\nimport org.yx.base.context.ActionContext;\nimport org.yx.base.sumk.UnsafeByteArrayOutputStream;\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.BizException;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.http.HttpHeaderName;\nimport org.yx.http.kit.HttpSettings;\nimport org.yx.http.kit.InnerHttpUtil;\nimport org.yx.http.log.HttpLogs;\nimport org.yx.log.Logs;\nimport org.yx.util.StringUtil;\nimport org.yx.util.UUIDSeed;\n\npublic abstract class AbstractLoginServlet implements LoginServlet {\n\n\tprivate static final String LOGIN_NAME = \"*login*\";\n\tprivate static final String NEW_SESSION_ID = \"sumk.http.session.id\";\n\tprivate UserSession session;\n\n\tprotected Logger logger() {\n\t\treturn Logs.http();\n\t}\n\n\t@Override\n\tpublic void service(HttpServletRequest req, HttpServletResponse resp) {\n\t\tfinal long beginTime = System.currentTimeMillis();\n\t\tThrowable ex = null;\n\t\tString user = null;\n\t\tCharset charset = InnerHttpUtil.charset(req);\n\t\ttry {\n\t\t\tInnerHttpUtil.startContext(req, resp, LOGIN_NAME);\n\t\t\tif (!acceptMethod(req, resp)) {\n\t\t\t\tlogger().warn(\"不是login的有效method，比如HEAD等方法可能不支持\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tInnerHttpUtil.setRespHeader(resp, charset);\n\t\t\tString sid = createSessionId(req);\n\t\t\tuser = getUserName(req);\n\t\t\tLoginObject obj = login(sid, user, req);\n\t\t\tif (obj == null) {\n\t\t\t\tlogger().warn(\"{} 没有关联到session对象\", sid);\n\t\t\t\tInnerHttpUtil.sendError(resp, HttpErrorCode.LOGINFAILED, \"登录失败\", charset);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (obj.getErrorMsg() != null) {\n\t\t\t\tInnerHttpUtil.sendError(resp, HttpErrorCode.LOGINFAILED, obj.getErrorMsg(), charset);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (obj.getSessionObject() == null || StringUtil.isEmpty(obj.getSessionObject().getUserId())) {\n\t\t\t\tlogger().warn(\"{} :sid:{} 未正确设置session对象，或者session对象中的userId为空\", user, sid);\n\t\t\t\tInnerHttpUtil.sendError(resp, HttpErrorCode.LOGINFAILED, \"登录失败\", charset);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tObject newSid = req.getAttribute(NEW_SESSION_ID);\n\t\t\tif (newSid instanceof String) {\n\t\t\t\tlogger().debug(\"change sid {} to {}\", sid, newSid);\n\t\t\t\tsid = (String) newSid;\n\t\t\t}\n\t\t\tkickout(obj, sid, req, resp);\n\n\t\t\tbyte[] key = createEncryptKey(req);\n\t\t\tif (!this.setSession(req, sid, obj, key)) {\n\t\t\t\tlogger().warn(\"{} :sid:{} login failed,maybe sessionId existed.\", user, sid);\n\t\t\t\tInnerHttpUtil.sendError(resp, HttpErrorCode.LOGINFAILED, \"登录失败\", charset);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString userId = obj.getSessionObject().getUserId();\n\t\t\tresp.setHeader(HttpHeaderName.sessionId(), sid);\n\t\t\tif (StringUtil.isNotEmpty(userId)) {\n\t\t\t\tresp.setHeader(HttpHeaderName.userFlag(), userId);\n\t\t\t}\n\n\t\t\tif (HttpSettings.isCookieEnable()) {\n\t\t\t\tString contextPath = req.getContextPath();\n\n\t\t\t\tif (!contextPath.startsWith(\"/\")) {\n\t\t\t\t\tcontextPath = \"/\" + contextPath;\n\t\t\t\t}\n\t\t\t\tString attr = \";Path=\".concat(contextPath);\n\t\t\t\tsetSessionCookie(req, resp, sid, attr);\n\t\t\t\tsetUserFlagCookie(req, resp, userId, attr);\n\t\t\t}\n\n\t\t\tUnsafeByteArrayOutputStream out = new UnsafeByteArrayOutputStream(64);\n\t\t\tthis.outputKey(out, key, req, resp);\n\t\t\tif (obj.getResponseData() != null) {\n\t\t\t\tout.write(obj.getResponseData().getBytes(charset));\n\t\t\t}\n\t\t\tresp.getOutputStream().write(out.toByteArray());\n\t\t} catch (Throwable e) {\n\t\t\tex = e;\n\t\t\tlogger().error(\"user:\" + user + \",message:\" + e.getLocalizedMessage(), e);\n\t\t\tif (e instanceof BizException) {\n\t\t\t\tBizException be = (BizException) e;\n\t\t\t\tInnerHttpUtil.sendError(resp, be.getCode(), be.getMessage(), charset);\n\t\t\t} else {\n\t\t\t\tInnerHttpUtil.sendError(resp, HttpErrorCode.LOGINFAILED, \"login fail:\" + user, charset);\n\t\t\t}\n\t\t} finally {\n\t\t\tlong time = System.currentTimeMillis() - beginTime;\n\t\t\tHttpLogs.log(null, req, ex, time);\n\t\t\tInnerHttpUtil.record(LOGIN_NAME, time, ex == null);\n\t\t\tActionContext.remove();\n\t\t}\n\n\t}\n\n\tprotected void kickout(LoginObject obj, String sid, HttpServletRequest req, HttpServletResponse resp) {\n\n\t}\n\n\tprotected boolean setSession(HttpServletRequest req, String sessionId, LoginObject obj, byte[] key) {\n\t\tif (req.getAttribute(NEW_SESSION_ID) instanceof String) {\n\t\t\treturn true;\n\t\t}\n\t\treturn this.session.setSession(sessionId, obj.getSessionObject(), key, HttpSettings.isSingleLogin());\n\t}\n\n\tprotected boolean acceptMethod(HttpServletRequest req, HttpServletResponse resp) throws IOException {\n\t\tif (!HttpSettings.defaultHttpMethods().contains(req.getMethod())) {\n\t\t\tresp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, req.getMethod() + \" not allowd\");\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprotected String getUserName(HttpServletRequest req) {\n\t\treturn req.getParameter(AppInfo.get(\"sumk.http.login.username\", \"username\"));\n\t}\n\n\tprotected void setSessionCookie(HttpServletRequest req, HttpServletResponse resp, final String sid, String attr) {\n\t\tStringBuilder cookie = new StringBuilder(64).append(HttpHeaderName.sessionId()).append('=').append(sid)\n\t\t\t\t.append(attr);\n\n\t\tresp.addHeader(\"Set-Cookie\", cookie.toString());\n\t}\n\n\tprotected void setUserFlagCookie(HttpServletRequest req, HttpServletResponse resp, String userId, String attr) {\n\t\tif (HttpSettings.isSingleLogin() && StringUtil.isNotEmpty(userId)) {\n\t\t\tStringBuilder cookie = new StringBuilder();\n\t\t\tcookie.append(HttpHeaderName.userFlag()).append('=').append(userId).append(attr);\n\t\t\tresp.addHeader(\"Set-Cookie\", cookie.toString());\n\t\t}\n\t}\n\n\tprotected void outputKey(OutputStream out, byte[] key, HttpServletRequest req, HttpServletResponse resp)\n\t\t\tthrows IOException {\n\t\tif (AppInfo.getBoolean(\"sumk.http.key.output.body\", false)) {\n\t\t\tout.write(S.base64().encode(key));\n\t\t\tout.write(new byte[] { '\\t', '\\n' });\n\t\t}\n\n\t\tif (AppInfo.getBoolean(\"sumk.http.key.output.header\", true)) {\n\t\t\tresp.setHeader(AppInfo.get(\"sumk.http.header.skey\", \"skey\"), S.base64().encodeToString(key));\n\t\t}\n\t}\n\n\tprotected byte[] createEncryptKey(HttpServletRequest req) {\n\t\treturn UUIDSeed.seq().substring(4).getBytes();\n\t}\n\n\tprotected String createSessionId(HttpServletRequest req) {\n\t\treturn UUIDSeed.random();\n\t}\n\n\t@Override\n\tpublic void init(ServletConfig config) {\n\t\tsession = WebSessions.loadUserSession();\n\t}\n\n\tprotected UserSession userSession() {\n\t\treturn session;\n\t}\n\n\t/**\n\t * 用于处理登陆业务\n\t * \n\t * @param sessionId http头部sid的信息\n\t * @param userName  用户名，默认通过request.getParameter(\"username\")获取的\n\t * @param req       用户请求的HttpServletRequest对象\n\t * @return 登录信息，无论成功与否，返回值不能是null\n\t * @throws Exception 异常与failed等价\n\t */\n\tprotected abstract LoginObject login(String sessionId, String userName, HttpServletRequest req) throws Exception;\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/AbstractUserSession.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.slf4j.Logger;\nimport org.yx.common.util.S;\nimport org.yx.http.kit.HttpSettings;\nimport org.yx.log.Log;\nimport org.yx.util.StringUtil;\nimport org.yx.util.SumkDate;\n\npublic abstract class AbstractUserSession implements UserSession {\n\tprotected final Logger log = Log.get(\"sumk.http.session\");\n\tprotected final ConcurrentMap<String, TimedCachedObject> cache = new ConcurrentHashMap<>();\n\n\tprotected abstract TimedCachedObject loadTimedCachedObject(String sessionId, boolean needRefresh);\n\n\tpublic abstract String getSessionIdByUserFlag(String userId);\n\n\t@Override\n\tpublic byte[] getEncryptKey(String sid) {\n\t\tTimedCachedObject obj = this.loadTimedCachedObject(sid, false);\n\t\tif (obj == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn obj.getKey();\n\t}\n\n\t@Override\n\tpublic <T extends SessionObject> T getUserObject(String sessionId, Class<T> clz) {\n\t\treturn this._getUserObject(sessionId, clz, false);\n\t}\n\n\t@Override\n\tpublic <T extends SessionObject> T loadAndRefresh(String sessionId, Class<T> clz) {\n\t\treturn this._getUserObject(sessionId, clz, true);\n\t}\n\n\tprivate <T extends SessionObject> T _getUserObject(String sessionId, Class<T> clz, boolean refreshTTL) {\n\t\tTimedCachedObject obj = this.loadTimedCachedObject(sessionId, refreshTTL);\n\t\tif (obj == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn S.json().fromJson(obj.json, clz);\n\t}\n\n\t@Override\n\tpublic String sessionId(String userId) {\n\t\tif (!HttpSettings.isSingleLogin()) {\n\t\t\tlog.warn(\"只有设置了sumk.http.session.single=1,本方法才有意义\");\n\t\t\treturn null;\n\t\t}\n\t\tif (StringUtil.isEmpty(userId)) {\n\t\t\treturn null;\n\t\t}\n\t\tString sessionId = this.getSessionIdByUserFlag(userId);\n\t\tif (sessionId == null) {\n\t\t\treturn null;\n\t\t}\n\t\tSessionObject obj = this.getUserObjectBySessionId(sessionId);\n\t\tif (obj == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (obj.getExpiredTime() == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (obj.getExpiredTime().longValue() < System.currentTimeMillis()) {\n\t\t\tlog.debug(\"该过期session的截止时间:{}\", SumkDate.of(obj.getExpiredTime()));\n\t\t\treturn null;\n\t\t}\n\t\treturn obj.getUserId();\n\t}\n\n\tprotected SessionObject getUserObjectBySessionId(String sessionId) {\n\t\tTimedCachedObject obj = this.loadTimedCachedObject(sessionId, false);\n\t\tif (obj == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn S.json().fromJson(obj.json, SessionObject.class);\n\t}\n\n\t@Override\n\tpublic int localCacheSize() {\n\t\treturn this.cache.size();\n\t}\n\n\tpublic Map<String, TimedCachedObject> localCache() {\n\t\treturn Collections.unmodifiableMap(cache);\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/CacheHelper.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.slf4j.Logger;\nimport org.yx.log.Log;\n\npublic final class CacheHelper {\n\tpublic static void expire(Map<String, TimedCachedObject> map, long duration) {\n\t\tLogger log = Log.get(\"sumk.http.session\");\n\t\ttry {\n\t\t\texpire0(map, duration, log);\n\t\t} catch (Exception e) {\n\t\t\tlog.error(e.getMessage(), e);\n\t\t}\n\t}\n\n\tprivate static void expire0(Map<String, TimedCachedObject> map, long duration, Logger log) {\n\t\tint beginSize = map.size();\n\t\tlong begin = System.currentTimeMillis() - duration;\n\t\tIterator<Entry<String, TimedCachedObject>> it = map.entrySet().iterator();\n\t\twhile (it.hasNext()) {\n\n\t\t\tEntry<String, TimedCachedObject> en = it.next();\n\t\t\tif (en == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tTimedCachedObject t = en.getValue();\n\t\t\tif (t == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t.refreshTime < begin) {\n\t\t\t\tlog.trace(\"{} remove from cache\", en.getKey());\n\t\t\t\tit.remove();\n\t\t\t}\n\t\t}\n\t\tif (log.isTraceEnabled()) {\n\t\t\tlog.trace(\"catch size from {} to {},duration:{}\", beginSize, map.size(), duration);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/HttpLoginWrapper.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\nimport java.util.List;\n\nimport javax.servlet.ServletConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.yx.bean.IOC;\nimport org.yx.http.server.AbstractCommonHttpServlet;\nimport org.yx.log.Logs;\n\npublic class HttpLoginWrapper extends AbstractCommonHttpServlet {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate LoginServlet serv;\n\n\tprotected void handle(HttpServletRequest req, HttpServletResponse resp) {\n\t\tserv.service(req, resp);\n\t}\n\n\t@Override\n\tpublic void init(ServletConfig config) throws ServletException {\n\t\tsuper.init(config);\n\t\ttry {\n\t\t\tList<LoginServlet> ss = IOC.getBeans(LoginServlet.class);\n\t\t\tif (ss == null || ss.isEmpty()) {\n\t\t\t\tLogs.http().info(\"there is no LoginServlet\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (ss.size() > 1) {\n\t\t\t\tLogs.http().warn(\"there is {} login servlet\", ss.size());\n\t\t\t}\n\t\t\tthis.serv = ss.get(0);\n\t\t\tserv.init(config);\n\t\t} catch (Exception e) {\n\t\t\tLogs.http().error(e.getLocalizedMessage(), e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/HttpSessionFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\npublic interface HttpSessionFactory {\n\tUserSession create();\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/LocalUserSession.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.TimeUnit;\n\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.http.kit.HttpSettings;\nimport org.yx.log.Logs;\nimport org.yx.util.Task;\n\npublic class LocalUserSession extends AbstractUserSession {\n\n\tprotected Map<String, String> userSessionMap = Collections\n\t\t\t.synchronizedMap(new LinkedHashMap<String, String>(128, 0.75f, true) {\n\t\t\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t\t\t@Override\n\t\t\t\tprotected boolean removeEldestEntry(Entry<String, String> eldest) {\n\t\t\t\t\treturn this.size() > AppInfo.getInt(\"sumk.http.localsession.maxSize\", 1000);\n\t\t\t\t}\n\n\t\t\t});\n\n\tpublic LocalUserSession() {\n\t\tlog.info(\"$$$use local user session\");\n\t\tlong seconds = AppInfo.getLong(\"sumk.http.session.period\", 30L);\n\t\tTask.scheduleAtFixedRate(() -> CacheHelper.expire(cache, HttpSettings.httpSessionTimeoutInMs()), seconds,\n\t\t\t\tseconds, TimeUnit.SECONDS);\n\t}\n\n\t@Override\n\tpublic boolean setSession(String sessionId, SessionObject sessionObj, byte[] key, boolean singleLogin) {\n\t\tTimedCachedObject to = new TimedCachedObject(S.json().toJson(sessionObj), key);\n\t\tto.refreshTime = System.currentTimeMillis();\n\t\tif (cache.putIfAbsent(sessionId, to) != null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!singleLogin) {\n\t\t\treturn true;\n\t\t}\n\t\tString oldSessionId = userSessionMap.put(sessionObj.getUserId(), sessionId);\n\t\tif (oldSessionId != null && !oldSessionId.equals(sessionId)) {\n\t\t\tcache.remove(oldSessionId);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprotected TimedCachedObject loadTimedCachedObject(String sid, boolean needRefresh) {\n\t\tif (sid == null) {\n\t\t\treturn null;\n\t\t}\n\t\tTimedCachedObject to = cache.get(sid);\n\t\tif (to == null) {\n\t\t\treturn null;\n\t\t}\n\t\tlong now = System.currentTimeMillis();\n\t\tif (to.refreshTime + HttpSettings.httpSessionTimeoutInMs() < now) {\n\t\t\tLogs.http().debug(\"session:{}实际上已经过期了\", sid);\n\t\t\tcache.remove(sid);\n\t\t\treturn null;\n\t\t}\n\t\tif (needRefresh) {\n\t\t\tto.refreshTime = now;\n\t\t}\n\t\treturn to;\n\t}\n\n\t@Override\n\tpublic void removeSession(String sessionId) {\n\t\tif (sessionId == null) {\n\t\t\treturn;\n\t\t}\n\t\tTimedCachedObject to = cache.remove(sessionId);\n\t\tif (to == null || userSessionMap.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tSessionObject obj = S.json().fromJson(to.json, SessionObject.class);\n\t\tthis.userSessionMap.remove(obj.userId);\n\t}\n\n\t@Override\n\tpublic String getSessionIdByUserFlag(String userId) {\n\t\treturn this.userSessionMap.get(userId);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/LoginObject.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\nimport java.util.Objects;\n\npublic class LoginObject {\n\n\tprivate final String errorMsg;\n\n\tprivate final String responseData;\n\n\tprivate final SessionObject sessionObj;\n\n\tpublic SessionObject getSessionObject() {\n\t\treturn sessionObj;\n\t}\n\n\tpublic LoginObject(String responseData, SessionObject sessionObj, String errorMsg) {\n\t\tthis.responseData = responseData;\n\t\tthis.sessionObj = sessionObj;\n\t\tthis.errorMsg = errorMsg;\n\t}\n\n\tpublic String getResponseData() {\n\t\treturn responseData;\n\t}\n\n\tpublic String getErrorMsg() {\n\t\treturn errorMsg;\n\t}\n\n\tpublic static LoginObject fail(String errorMsg) {\n\t\treturn new LoginObject(null, null, errorMsg);\n\t}\n\n\tpublic static LoginObject success(String responseData, SessionObject sessionObj) {\n\t\treturn new LoginObject(responseData,\n\t\t\t\tObjects.requireNonNull(sessionObj, \"sessionObject cannot be null when login successed\"), null);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/LoginServlet.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\nimport javax.servlet.ServletConfig;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.yx.base.Ordered;\n\npublic interface LoginServlet extends Ordered {\n\n\tvoid init(ServletConfig config);\n\n\tvoid service(HttpServletRequest req, HttpServletResponse resp);\n\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/RemoteUserSession.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\nimport java.util.concurrent.TimeUnit;\n\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.http.kit.HttpSettings;\nimport org.yx.redis.Redis;\nimport org.yx.util.StringUtil;\nimport org.yx.util.Task;\n\npublic class RemoteUserSession extends AbstractUserSession {\n\tprivate static final byte[] NX = { 'N', 'X' };\n\tprivate static final byte[] PX = { 'P', 'X' };\n\n\tprotected long noFreshTime = AppInfo.getLong(\"sumk.http.session.cache.nofreshtime\", 1000L * 2);\n\n\tprotected final int maxSize;\n\n\tprotected final Redis redis;\n\n\tpublic RemoteUserSession(Redis redis) {\n\t\tthis.redis = redis.isMuted() ? redis : redis.mute();\n\t\tthis.maxSize = Math.max(AppInfo.getInt(\"sumk.http.session.cache.maxsize\", 5000), 10);\n\t\tlong seconds = AppInfo.getLong(\"sumk.http.session.period\", 30L);\n\t\tTask.scheduleAtFixedRate(() -> {\n\n\t\t\tlong duration = AppInfo.getLong(\"sumk.http.session.remote.duration\", 1000L * 60);\n\t\t\tif (duration > HttpSettings.httpSessionTimeoutInMs()) {\n\t\t\t\tduration = HttpSettings.httpSessionTimeoutInMs();\n\t\t\t}\n\t\t\tCacheHelper.expire(cache, duration);\n\t\t}, seconds, seconds, TimeUnit.SECONDS);\n\t}\n\n\tprotected final String singleKey(String userId) {\n\t\treturn \"_SINGLE_SES_\".concat(userId);\n\t}\n\n\tprotected final byte[] redisSessionKey(String sessionId) {\n\t\tif (sessionId == null || sessionId.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (\"_SES_#\" + sessionId).getBytes(AppInfo.UTF8);\n\t}\n\n\tprivate void removeOne() {\n\t\ttry {\n\t\t\tString key = cache.keySet().iterator().next();\n\t\t\tlog.debug(\"remove session {} from local cache\", key);\n\t\t\tcache.remove(key);\n\t\t} catch (Exception e) {\n\t\t\tlog.error(e.getMessage(), e);\n\t\t}\n\t}\n\n\t@Override\n\tprotected TimedCachedObject loadTimedCachedObject(String sid, boolean needRefresh) {\n\t\tif (sid == null) {\n\t\t\treturn null;\n\t\t}\n\t\tbyte[] bigKey = this.redisSessionKey(sid);\n\t\tTimedCachedObject to = cache.get(sid);\n\t\tlong now = System.currentTimeMillis();\n\t\tif (to == null) {\n\t\t\tbyte[] bv = redis.get(bigKey);\n\t\t\tto = TimedCachedObject.deserialize(bv);\n\t\t\tif (to == null) {\n\t\t\t\tif (log.isTraceEnabled()) {\n\t\t\t\t\tlog.trace(\"{} cannot found from redis\", sid);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (log.isTraceEnabled()) {\n\t\t\t\tlog.trace(\"{} add to local cache\", sid);\n\t\t\t}\n\t\t\tif (cache.size() >= this.maxSize) {\n\t\t\t\tremoveOne();\n\t\t\t}\n\t\t\tcache.put(sid, to);\n\t\t}\n\n\t\tif (needRefresh && to.refreshTime + this.noFreshTime < now) {\n\t\t\tlong durationInMS = HttpSettings.httpSessionTimeoutInMs();\n\t\t\tLong v = redis.pexpire(bigKey, durationInMS);\n\t\t\tif (v != null && v.longValue() == 0) {\n\t\t\t\tcache.remove(sid);\n\t\t\t\tlog.trace(\"{} was pexpire by redis,and remove from local cache\", sid);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (log.isTraceEnabled()) {\n\t\t\t\tlog.trace(\"{} was refresh in redis\", sid);\n\t\t\t}\n\t\t\tto.refreshTime = now;\n\t\t}\n\t\treturn to;\n\t}\n\n\t@Override\n\tpublic void removeSession(String sessionId) {\n\t\tbyte[] bigKey = this.redisSessionKey(sessionId);\n\t\tif (bigKey == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (HttpSettings.isSingleLogin()) {\n\t\t\tSessionObject t = getUserObjectBySessionId(sessionId);\n\t\t\tif (t != null) {\n\t\t\t\tredis.del(this.singleKey(t.getUserId()));\n\t\t\t}\n\t\t}\n\t\tthis.cache.remove(sessionId);\n\t\tredis.del(bigKey);\n\t}\n\n\t@Override\n\tpublic boolean setSession(String sessionId, SessionObject sessionObj, byte[] key, boolean singleLogin) {\n\t\tlong sessionTimeout = HttpSettings.httpSessionTimeoutInMs();\n\t\tbyte[] bigKey = this.redisSessionKey(sessionId);\n\t\tString json = S.json().toJson(sessionObj);\n\t\tbyte[] data = TimedCachedObject.toBytes(json, key);\n\t\tString ret = redis.set(bigKey, data, NX, PX, sessionTimeout);\n\t\tif (!\"OK\".equalsIgnoreCase(ret) && !\"1\".equals(ret)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!singleLogin) {\n\t\t\treturn true;\n\t\t}\n\n\t\tString singleUserKey = singleKey(sessionObj.userId);\n\t\tString oldSessionId = redis.get(singleUserKey);\n\t\tif (StringUtil.isNotEmpty(oldSessionId) && !oldSessionId.equals(sessionId)) {\n\t\t\tredis.del(redisSessionKey(oldSessionId));\n\t\t\tcache.remove(oldSessionId);\n\t\t}\n\t\tlong expireTime = sessionObj.getExpiredTime() != null ? sessionObj.getExpiredTime() - System.currentTimeMillis()\n\t\t\t\t: -1;\n\t\tif (expireTime < 1) {\n\n\t\t\texpireTime = AppInfo.getLong(\"sumk.http.session.single.maxTime\", 1000L * 3600 * 20);\n\t\t}\n\t\tredis.psetex(singleUserKey, expireTime, sessionId);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getSessionIdByUserFlag(String userId) {\n\t\treturn redis.get(this.singleKey(userId));\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/SessionObject.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\n/**\n * 用户session,要有无构造参数的构造函数。\n */\npublic class SessionObject {\n\n\tprotected String userId;\n\tprivate Long expiredTime;\n\n\t/**\n\t * 返回用户id\n\t * \n\t * @return 用户id，不能为null\n\t */\n\tpublic String getUserId() {\n\t\treturn userId;\n\t}\n\n\tpublic void setUserId(String userId) {\n\t\tthis.userId = userId;\n\t}\n\n\tpublic Long getExpiredTime() {\n\t\treturn expiredTime;\n\t}\n\n\t/**\n\t * 如果设置了过期时间，即使用户一直在操作， 当达到过期时间后，session也会被清理\n\t * \n\t * @param expiredTime 最大的过期时间，精确到毫秒。null表示不自动清理\n\t */\n\tpublic void setExpiredTime(Long expiredTime) {\n\t\tthis.expiredTime = expiredTime;\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/TimedCachedObject.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\nimport java.util.Arrays;\nimport java.util.Objects;\n\nimport org.yx.conf.AppInfo;\n\npublic class TimedCachedObject {\n\tlong refreshTime;\n\tfinal String json;\n\tfinal byte[] key;\n\n\tpublic TimedCachedObject(String json, byte[] key) {\n\t\tthis.json = Objects.requireNonNull(json);\n\t\tthis.key = Objects.requireNonNull(key);\n\t}\n\n\tpublic long getRefreshTime() {\n\t\treturn refreshTime;\n\t}\n\n\tpublic String getJson() {\n\t\treturn json;\n\t}\n\n\tpublic byte[] getKey() {\n\t\treturn key;\n\t}\n\n\tpublic void setRefreshTime(long refreshTime) {\n\t\tthis.refreshTime = refreshTime;\n\t}\n\n\tpublic final boolean isExpired(long duration, long now) {\n\t\treturn this.refreshTime + duration < now;\n\t}\n\n\tpublic static byte[] toBytes(String json, byte[] key) {\n\t\tint keyLength = key.length;\n\t\tint b1 = keyLength & 0xFF;\n\t\tint b0 = (keyLength >> 8) & 0xFF;\n\t\tbyte[] j = json.getBytes(AppInfo.UTF8);\n\t\tbyte[] ret = new byte[2 + keyLength + j.length];\n\t\tret[0] = (byte) b0;\n\t\tret[1] = (byte) b1;\n\t\tSystem.arraycopy(key, 0, ret, 2, keyLength);\n\t\tSystem.arraycopy(j, 0, ret, 2 + keyLength, j.length);\n\t\treturn ret;\n\t}\n\n\tpublic static TimedCachedObject deserialize(byte[] bv) {\n\t\tif (bv == null || bv.length < 3) {\n\t\t\treturn null;\n\t\t}\n\n\t\tint keyLength = (bv[0] & 0xff) << 8 | (bv[1] & 0xff);\n\t\tString json = new String(bv, 2 + keyLength, bv.length - 2 - keyLength, AppInfo.UTF8);\n\t\treturn new TimedCachedObject(json, Arrays.copyOfRange(bv, 2, 2 + keyLength));\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/UserSession.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\npublic interface UserSession {\n\n\tbyte[] getEncryptKey(String sessionId);\n\n\t<T extends SessionObject> T getUserObject(String sessionId, Class<T> clz);\n\n\t<T extends SessionObject> T loadAndRefresh(String sessionId, Class<T> clz);\n\n\tboolean setSession(String sessionId, SessionObject sessionObj, byte[] key, boolean singleLogin);\n\n\tvoid removeSession(String sessionId);\n\n\tString sessionId(String userId);\n\n\tint localCacheSize();\n}\n"
  },
  {
    "path": "sumk-http/src/main/java/org/yx/http/user/WebSessions.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.http.user;\n\nimport java.util.Objects;\nimport java.util.function.Predicate;\n\nimport org.yx.bean.IOC;\nimport org.yx.exception.BizException;\nimport org.yx.http.HttpErrorCode;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.redis.Redis;\nimport org.yx.redis.RedisLoader;\nimport org.yx.redis.RedisPool;\n\npublic final class WebSessions {\n\tprivate static UserSession session;\n\n\tprivate static Predicate<String> sessionIdVerifier = s -> s != null && s.length() > 10;\n\n\tpublic static UserSession userSession() {\n\t\treturn session;\n\t}\n\n\tpublic static UserSession loadUserSession() {\n\t\tif (session == null) {\n\t\t\tLog.get(\"sumk.http.session\").info(\"session has not created\");\n\t\t\tthrow BizException.create(HttpErrorCode.SESSION_ERROR, \"请重新登录\");\n\t\t}\n\t\treturn session;\n\t}\n\n\tpublic static Predicate<String> getSessionIdVerifier() {\n\t\treturn sessionIdVerifier;\n\t}\n\n\tpublic static void setSessionIdVerifier(Predicate<String> sessionIdVerifier) {\n\t\tWebSessions.sessionIdVerifier = Objects.requireNonNull(sessionIdVerifier);\n\t}\n\n\tpublic static <T extends SessionObject> T getUserObject(String sessionId, Class<T> clz) {\n\t\treturn loadUserSession().getUserObject(sessionId, clz);\n\t}\n\n\tpublic static void remove(String sessionId) {\n\t\tuserSession();\n\t\tif (session == null) {\n\t\t\tLog.get(\"sumk.http.session\").debug(\"has removed\");\n\t\t\treturn;\n\t\t}\n\t\tsession.removeSession(sessionId);\n\t}\n\n\tpublic static synchronized void initSession() {\n\t\tif (session != null) {\n\t\t\treturn;\n\t\t}\n\t\tHttpSessionFactory factory = IOC.get(HttpSessionFactory.class);\n\t\tsession = factory != null ? factory.create() : createDefaultUserSession();\n\t}\n\n\tprivate static UserSession createDefaultUserSession() {\n\t\ttry {\n\t\t\tRedis redis = RedisPool.getRedisExactly(RedisLoader.SESSION);\n\t\t\treturn redis == null ? new LocalUserSession() : new RemoteUserSession(redis);\n\t\t} catch (NoClassDefFoundError e) {\n\t\t\tLogs.http().debug(\"use local session because redis cannot load\", e);\n\t\t\treturn new LocalUserSession();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-http/src/main/resources/META-INF/http/listeners",
    "content": "javax.servlet.ServletContextAttributeListener\njavax.servlet.ServletRequestListener\njavax.servlet.ServletRequestAttributeListener\njavax.servlet.http.HttpSessionListener\njavax.servlet.http.HttpSessionAttributeListener"
  },
  {
    "path": "sumk-http/src/main/resources/META-INF/sumk.factories",
    "content": "sumk.ioc.bean=org.yx.http.handler.Base64DecodeHandler,org.yx.http.handler.Base64EncodeHandler,\\\n\torg.yx.http.handler.DecryptHandler,org.yx.http.handler.EncryptHandler,\\\n\torg.yx.http.handler.InvokeHandler,org.yx.http.handler.MultipartHandler,\\\n\torg.yx.http.handler.ReqDataHandler,org.yx.http.handler.ReqSignValidateHandler,\\\n\torg.yx.http.handler.ReqToStringHandler,org.yx.http.handler.ReqUserHandler,\\\n\torg.yx.http.handler.RespBodyHandler,org.yx.http.handler.RespToStringHandler,\\\n\torg.yx.http.handler.ToBytesHandler,\\\n\torg.yx.http.server.DocumentServlet,org.yx.http.server.MultipartServer,\\\n\torg.yx.http.server.RestServer,org.yx.http.server.SumkMonitor,\\\n\torg.yx.http.HttpPlugin\nsumk.ioc.optional=org.yx.http.*"
  },
  {
    "path": "sumk-rpc/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright youtongluan (\\u6e38\\u901a\\u92ae\\uff0c\\u522b\\u540d\\uff1a\\u6e38\\u590f)\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "sumk-rpc/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.github.youtongluan</groupId>\n\t\t<artifactId>sumk</artifactId>\n\t\t<version>4.2.1</version>\n\t</parent>\n\t<artifactId>sumk-rpc</artifactId>\n\t<name>com.github.youtongluan:sumk</name>\n\t<description>A quick developing framewort for internet company</description>\n\t<url>https://github.com/youtongluan/sumk</url>\n\t<licenses>\n\t\t<license>\n\t\t\t<name>The Apache License, Version 2.0</name>\n\t\t\t<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n\t\t</license>\n\t</licenses>\n\t<scm>\n\t\t<url>https://github.com/youtongluan/sumk</url>\n\t\t<connection>https://github.com/youtongluan/sumk.git</connection>\n\t\t<developerConnection>https://github.com/youtongluan/sumk</developerConnection>\n\t</scm>\n\t<distributionManagement>\n\t    <repository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n\t    </repository>\n\t    <snapshotRepository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n\t    </snapshotRepository>\n\t</distributionManagement>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>sumk-framework</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t  <groupId>org.apache.zookeeper</groupId>\n\t\t  <artifactId>zookeeper</artifactId>\n\t\t  <exclusions>\n\t\t  \t<exclusion>\n\t\t  \t\t<groupId>io.netty</groupId>\n\t\t  \t\t<artifactId>netty-handler</artifactId>\n\t\t  \t</exclusion>\n\t\t  \t<exclusion>\n\t\t  \t\t<groupId>org.slf4j</groupId>\n\t\t  \t\t<artifactId>slf4j-log4j12</artifactId>\n\t\t  \t</exclusion>\n\t\t  \t<exclusion>\n\t\t  \t\t<groupId>io.netty</groupId>\n\t\t  \t\t<artifactId>netty-transport-native-epoll</artifactId>\n\t\t  \t</exclusion>\n\t\t  \t<exclusion>\n\t\t  \t\t<groupId>log4j</groupId>\n\t\t  \t\t<artifactId>log4j</artifactId>\n\t\t  \t</exclusion>\n\t\t  \t<exclusion>\n\t\t  \t\t<groupId>ch.qos.logback</groupId>\n\t\t  \t\t<artifactId>logback-core</artifactId>\n\t\t  \t</exclusion>\n\t\t  \t<exclusion>\n\t\t  \t\t<groupId>ch.qos.logback</groupId>\n\t\t  \t\t<artifactId>logback-classic</artifactId>\n\t\t  \t</exclusion>\n\t\t  </exclusions>\n\t\t</dependency>\n\t\t<dependency>\n\t\t  <groupId>io.netty</groupId>\n\t\t  <artifactId>netty-handler</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.101tec</groupId>\n\t\t\t<artifactId>zkclient</artifactId>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<artifactId>netty</artifactId>\n\t\t\t\t\t<groupId>io.netty</groupId>\n\t\t\t\t</exclusion>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<artifactId>log4j</artifactId>\n\t\t\t\t\t<groupId>log4j</groupId>\n\t\t\t\t</exclusion>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<artifactId>slf4j-log4j12</artifactId>\n\t\t\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>"
  },
  {
    "path": "sumk-rpc/rpc.md",
    "content": "## 客户端\n\n### 建立连接\n\n1. Rpc.init()初始化Transport\n\n2. 请求某个地址的时候，视情况创建TransportClient\n\n3. TransportClient创建TransportChannel而TransportChannel实例创建之前，会创建mina的session或netty的channel。并且将setAttribute(Client.getName()，Client)，setAttribute(TransportChannel.getName()，channel)。这样session就跟channel和client关联起来\n\n### 断开连接\n\n* 外部通过TransportClient来关闭。\n\n* ClientHandler中只是通过Channel来关闭就行了。因为框架有定时监测idle的功能，所以被意外关闭的无需在意，让定时器来监测就好\n* 各个实现类要设法监听inputClose方法，它去close session\n\n   \n\n## 服务端\n\n### 接收连接\n\n1. SoaPlugin初始化Transport\n2.Transport初始化TransportServer\n\n2. Handler第一次接收session数据的时候，创建TransportChannel，并且setAttribute(TransportChannel.getName()，channel)\n\n\n### 断开连接\n\n* server只有idle、异常会主动断开连接\n\n* 各个实现类要设法监听inputClose方法，它去close session"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/annotation/rpc/Soa.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.rpc;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target({ ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Soa {\n\t/**\n\t * 在@SoaClass注解中，这个参数会被忽略\n\t * \n\t * @return 服务名称，如果为空，就使用method name。\n\t */\n\tString value() default \"\";\n\n\tString cnName() default \"\";\n\n\t/**\n\t * 发布的服务名称是否加上appId前缀，只在appId不为空的时候才有作用\n\t * \n\t * @return appId前缀\n\t */\n\tboolean appIdPrefix() default true;\n\n\tint toplimit() default 0;\n\n\t/**\n\t * 可以通过sumk.rpc.publish.[api名称]配置来覆盖本属性。<BR>\n\t * 设置sumk.rpc.server.register=0可以将整个应用的zk注册给禁用掉\n\t * \n\t * @return 是否将接口发布到zk上\n\t */\n\tboolean publish() default true;\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/annotation/rpc/SoaClass.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.rpc;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target({ ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface SoaClass {\n\n\t/**\n\t * 发布使用微服务使用的类型，一般是它继承的接口。 如果该类没有实现接口，或者只有一个接口，可以使用默认的Void\n\t * \n\t * @return 当前类、它实现的接口、它继承的超类\n\t */\n\tClass<?> refer() default Void.class;\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/annotation/rpc/SoaClientConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.annotation.rpc;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 仅对使用接口调用的rpc客户端有作用,注解作用在接口上面。 它用于定义额外的soa参数\n */\n@Target({ ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface SoaClientConfig {\n\n\tint timeout() default -1;\n\n\tint tryCount() default -1;\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/BusinessHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc;\n\nimport org.yx.rpc.transport.TransportChannel;\n\npublic interface BusinessHandler {\n\n\tvoid received(TransportChannel channel, Object message);\n\n\tvoid exceptionCaught(TransportChannel channel, Throwable exception);\n\n\tvoid closed(TransportChannel channel);\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/Profile.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\n\nimport org.yx.rpc.codec.Protocols;\n\npublic final class Profile {\n\tpublic static final Charset UTF8 = StandardCharsets.UTF_8;\n\tpublic static final int version = 0x160;\n\n\tpublic static long feature() {\n\t\tlong v = version;\n\t\tv <<= 32;\n\t\tv |= Protocols.profile() & 0xFFFFFFFFL;\n\t\treturn v;\n\t}\n\n\tpublic static String featureInHex() {\n\t\treturn Long.toHexString(feature());\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/RpcErrorCode.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc;\n\npublic interface RpcErrorCode {\n\n\t/**\n\t * 线程数溢出\n\t */\n\tString THREAD_THRESHOLD_OVER = \"700\";\n\n\tString WAIT_TWICE = \"710\";\n\n\t/**\n\t * 该api没有配置路由信息\n\t */\n\tString NO_ROUTE = \"720\";\n\n\t/**\n\t * 存在节点，但是节点不能用\n\t */\n\tString NO_NODE_AVAILABLE = \"730\";\n\n\t/**\n\t * 客户端等待超时\n\t */\n\tString TIMEOUT = \"740\";\n\n\t/**\n\t * 客户端发送失败，这个code会触发失败重试。 如果开启了失败重试，客户端一般不会得到这个异常码，而是NO_NODE_AVAILABLE\n\t */\n\tString SEND_FAILED = \"750\";\n\n\t/**\n\t * 客户端参数类型错误、服务器端业务代码出错等，并且异常类型不是BizException\n\t */\n\tString SERVER_HANDLE_ERROR = \"760\";\n\n\t/**\n\t * 服务器端未知出错\n\t */\n\tString SERVER_UNKNOW = \"770\";\n\n\t/**\n\t * 没有合适的handler\n\t */\n\tString NO_MAPPED_UNKNOW = \"777\";\n\n\t/**\n\t * 客户端的未知错误\n\t */\n\tString UNKNOW = \"799\";\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/RpcJson.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc;\n\nimport java.util.Objects;\n\nimport org.yx.annotation.ExcludeFromParams;\nimport org.yx.common.json.GsonHelper;\nimport org.yx.common.json.GsonOperator;\nimport org.yx.common.json.JsonOperator;\nimport org.yx.common.json.ServerJsonExclusionStrategy;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\n\npublic final class RpcJson {\n\n\tprivate static GsonBuilder gsonBuilder() {\n\n\t\treturn GsonHelper.builder(\"sumk.rpc\");\n\t}\n\n\tprivate static Gson createServerGson() {\n\t\treturn ServerJsonExclusionStrategy.addServerExclusionStrategy(gsonBuilder()).create();\n\t}\n\n\tprivate static Gson createClientGson() {\n\t\treturn gsonBuilder().addSerializationExclusionStrategy(new ServerJsonExclusionStrategy(ExcludeFromParams.class))\n\t\t\t\t.create();\n\t}\n\n\tprivate static JsonOperator server = new GsonOperator(createServerGson());\n\n\tprivate static JsonOperator client = new GsonOperator(createClientGson());\n\n\tprivate static JsonOperator operator = new GsonOperator(gsonBuilder().create());\n\n\tpublic static JsonOperator operator() {\n\t\treturn operator;\n\t}\n\n\tpublic static void setOperator(JsonOperator operator) {\n\t\tRpcJson.operator = Objects.requireNonNull(operator);\n\t}\n\n\tpublic static JsonOperator server() {\n\t\treturn server;\n\t}\n\n\tpublic static void setServerOperator(JsonOperator operator) {\n\t\tRpcJson.server = Objects.requireNonNull(operator);\n\t}\n\n\tpublic static JsonOperator client() {\n\t\treturn client;\n\t}\n\n\tpublic static void setClientOperator(JsonOperator operator) {\n\t\tRpcJson.client = Objects.requireNonNull(operator);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/RpcSettings.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc;\n\nimport org.yx.conf.AppInfo;\n\npublic final class RpcSettings {\n\tprivate static boolean started;\n\tprivate static boolean serverLogDisable;\n\tprivate static boolean clientLogDisable;\n\tprivate static long infoTime;\n\tprivate static long warnTime;\n\tprivate static int clientDefaultTimeout;\n\tprivate static boolean disableLocalRoute;\n\tprivate static int clientTryCount;\n\tprivate static boolean showServerExceptionLog;\n\tprivate static int maxReqLogSize;\n\tprivate static int maxRespLogSize;\n\n\tpublic static int maxReqLogSize() {\n\t\treturn maxReqLogSize;\n\t}\n\n\tpublic static int maxRespLogSize() {\n\t\treturn maxRespLogSize;\n\t}\n\n\tpublic static boolean showServerExceptionLog() {\n\t\treturn showServerExceptionLog;\n\t}\n\n\tpublic static boolean disableLocalRoute() {\n\t\treturn disableLocalRoute;\n\t}\n\n\tpublic static int clientDefaultTimeout() {\n\t\treturn clientDefaultTimeout;\n\t}\n\n\tpublic static long infoTime() {\n\t\treturn infoTime;\n\t}\n\n\tpublic static long warnTime() {\n\t\treturn warnTime;\n\t}\n\n\tpublic static boolean isServerLogDisable() {\n\t\treturn serverLogDisable;\n\t}\n\n\tpublic static int clientTryCount() {\n\t\treturn clientTryCount;\n\t}\n\n\tpublic static boolean isClientLogDisable() {\n\t\treturn clientLogDisable;\n\t}\n\n\tpublic static long maxServerIdleTime() {\n\t\treturn AppInfo.getLong(\"sumk.rpc.server.idle\", 1000L * 60 * 10);\n\t}\n\n\tpublic static long maxClientIdleTime() {\n\t\treturn AppInfo.getLong(\"sumk.rpc.client.idle\", 1000L * 60 * 5);\n\t}\n\n\tpublic synchronized static void init() {\n\t\tif (started) {\n\t\t\treturn;\n\t\t}\n\t\tstarted = true;\n\t\tAppInfo.addObserver(info -> {\n\t\t\tRpcSettings.warnTime = AppInfo.getInt(\"sumk.rpc.log.warn.time\", 3000);\n\t\t\tRpcSettings.infoTime = AppInfo.getInt(\"sumk.rpc.log.info.time\", 1000);\n\t\t\tRpcSettings.serverLogDisable = AppInfo.getBoolean(\"sumk.rpc.log.server.disable\", false);\n\t\t\tRpcSettings.clientLogDisable = AppInfo.getBoolean(\"sumk.rpc.log.client.disable\", false);\n\t\t\tRpcSettings.clientDefaultTimeout = AppInfo.getInt(\"sumk.rpc.call.timeout\", 30000);\n\t\t\tRpcSettings.clientTryCount = AppInfo.getInt(\"sumk.rpc.client.trycount\", 3);\n\t\t\tRpcSettings.disableLocalRoute = AppInfo.getBoolean(\"sumk.rpc.localroute.disable\", false);\n\t\t\tRpcSettings.showServerExceptionLog = AppInfo.getBoolean(\"sumk.rpc.server.exceptionlog\", false);\n\t\t\tRpcSettings.maxReqLogSize = AppInfo.getInt(\"sumk.rpc.log.reqsize\", 1000);\n\t\t\tRpcSettings.maxRespLogSize = AppInfo.getInt(\"sumk.rpc.log.respsize\", 5000);\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/RpcUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.common.Host;\n\nimport org.yx.base.context.ActionContext;\nimport org.yx.common.route.Router;\nimport org.yx.rpc.registry.client.RpcRoutes;\nimport org.yx.rpc.server.LocalRpcContext;\nimport org.yx.rpc.server.RpcContext;\n\npublic final class RpcUtil {\n\n\tpublic static String userId() {\n\t\treturn ActionContext.current().userId();\n\t}\n\n\tpublic static void setUserId(String userId) {\n\t\tActionContext.current().userId(userId);\n\t}\n\n\tpublic static boolean setUserIdIfEmpty(String userId) {\n\t\tActionContext tc = ActionContext.current();\n\t\tif (tc.userId() == null) {\n\t\t\ttc.userId(userId);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static Map<String, String> attachmentView() {\n\t\treturn ActionContext.current().attachmentView();\n\t}\n\n\tpublic static void setAttachment(String key, String value) {\n\t\tActionContext.current().setAttachment(key, value);\n\t}\n\n\tpublic static String getAttachment(String key) {\n\t\treturn ActionContext.current().getAttachment(key);\n\t}\n\n\tpublic static void removeContext() {\n\t\tActionContext.remove();\n\t}\n\n\tpublic static List<Host> allServers(String api) {\n\t\tRouter<Host> route = RpcRoutes.getRoute(api);\n\t\tif (route == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn route.allSources();\n\t}\n\n\tpublic static List<Host> aliveServers(String api) {\n\t\tRouter<Host> route = RpcRoutes.getRoute(api);\n\t\tif (route == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn route.aliveSources();\n\t}\n\n\tpublic static RpcContext context() {\n\t\treturn LocalRpcContext.getCtx();\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/AbstractRpcFuture.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport java.util.Objects;\n\nimport org.yx.base.context.LogContext;\nimport org.yx.common.Host;\nimport org.yx.common.util.S;\nimport org.yx.exception.CodeException;\n\npublic abstract class AbstractRpcFuture implements RpcFuture {\n\n\tprotected final RpcLocker locker;\n\n\tpublic AbstractRpcFuture(RpcLocker locker) {\n\t\tthis.locker = Objects.requireNonNull(locker);\n\t}\n\n\t@Override\n\tpublic <T> T getOrException(Class<T> clz) throws CodeException {\n\t\tString json = this.getOrException();\n\t\tif (json == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn S.json().fromJson(json, clz);\n\t}\n\n\t@Override\n\tpublic String getOrException() throws CodeException {\n\t\tRpcResult resp = this.awaitForRpcResult();\n\t\tresp.throwIfException();\n\t\treturn resp.json();\n\t}\n\n\t@Override\n\tpublic <T> T opt(Class<T> clz) throws CodeException {\n\t\tString json = this.opt();\n\t\tif (json == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn S.json().fromJson(json, clz);\n\t}\n\n\t@Override\n\tpublic String opt() throws CodeException {\n\t\tRpcResult resp = this.awaitForRpcResult();\n\t\treturn resp.json();\n\t}\n\n\t@Override\n\tpublic Host getServer() {\n\t\treturn this.locker.url();\n\t}\n\n\tpublic LogContext getOriginLogContext() {\n\t\treturn this.locker.originLogContext();\n\t}\n\n\t@Override\n\tpublic String getRequestId() {\n\t\treturn this.locker.req.getSn();\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/AbstractTransportClient.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport java.util.Objects;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.yx.common.Host;\nimport org.yx.log.Logs;\nimport org.yx.rpc.transport.RpcWriteFuture;\nimport org.yx.rpc.transport.TransportChannel;\nimport org.yx.rpc.transport.TransportClient;\n\npublic abstract class AbstractTransportClient implements TransportClient {\n\n\tprotected volatile TransportChannel channel;\n\tprotected final Host addr;\n\tprotected final Lock lock = new ReentrantLock();\n\n\tpublic AbstractTransportClient(Host host) {\n\t\tthis.addr = Objects.requireNonNull(host);\n\t}\n\n\tprivate boolean ensureSession() {\n\t\tif (channel != null && !channel.isClosing()) {\n\t\t\treturn true;\n\t\t}\n\t\ttry {\n\t\t\tconnect();\n\t\t} catch (Exception e1) {\n\t\t\tLogs.rpc().error(this.addr + \" - \" + e1.toString(), e1);\n\t\t}\n\n\t\tif (channel == null || channel.isClosing()) {\n\t\t\tHostChecker.get().addDownUrl(addr);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprotected abstract void connect() throws Exception;\n\n\t@Override\n\tpublic RpcWriteFuture write(Req req) {\n\t\tif (!this.ensureSession()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this.channel.write(req);\n\t}\n\n\t@Override\n\tpublic void closeIfPossibble() {\n\t\tTransportChannel s = null;\n\t\tif (lock.tryLock()) {\n\t\t\ttry {\n\t\t\t\ts = this.channel;\n\t\t\t\tthis.channel = null;\n\t\t\t} finally {\n\t\t\t\tlock.unlock();\n\t\t\t}\n\t\t} else {\n\t\t\tLogs.rpc().warn(\"关闭rpc连接时获取锁失败-{}\", this);\n\t\t\treturn;\n\t\t}\n\t\tif (s != null && s.isConnected()) {\n\t\t\ts.closeOnFlush();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIdle() {\n\t\tTransportChannel s = this.channel;\n\t\treturn s == null || s.isClosing();\n\t}\n\n\t@Override\n\tpublic final Host getRemoteAddr() {\n\t\treturn this.addr;\n\t}\n\n\t@Override\n\tpublic final TransportChannel getChannel() {\n\t\treturn this.channel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.getClass().getName() + \" [addr=\" + addr + \", session=\" + channel + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/Client.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Consumer;\n\nimport org.yx.common.Host;\nimport org.yx.base.context.ActionContext;\nimport org.yx.common.route.Router;\nimport org.yx.exception.SoaException;\nimport org.yx.log.Logs;\nimport org.yx.rpc.RpcErrorCode;\nimport org.yx.rpc.RpcJson;\nimport org.yx.rpc.RpcSettings;\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.context.InnerRpcUtil;\nimport org.yx.rpc.context.RpcActionNode;\nimport org.yx.rpc.context.RpcActions;\nimport org.yx.rpc.registry.client.RpcRoutes;\nimport org.yx.rpc.server.LocalRequestHandler;\nimport org.yx.rpc.server.Response;\nimport org.yx.rpc.transport.RpcWriteFuture;\nimport org.yx.rpc.transport.TransportClient;\nimport org.yx.util.UUIDSeed;\n\npublic final class Client {\n\n\tprivate static final Host LOCAL = Host.create(\"local\", 0);\n\tprivate static final AtomicInteger COUNTER = new AtomicInteger();\n\tprivate final String api;\n\tprivate Object params;\n\tprivate ParamType paramType;\n\tprivate int totalTimeout;\n\n\tprivate Host[] directUrls;\n\n\tprivate boolean backup;\n\tprivate int tryCount;\n\tprivate Consumer<RpcCallInfo> callback;\n\n\tpublic Client(String api) {\n\t\tthis.api = Objects.requireNonNull(api).trim();\n\t\tthis.totalTimeout = RpcSettings.clientDefaultTimeout();\n\t\tthis.tryCount = RpcSettings.clientTryCount();\n\t}\n\n\tpublic Client directUrls(Host... urls) {\n\t\tthis.directUrls = urls;\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置发送的尝试次数。只有发送失败会重试，其它的不会\n\t * \n\t * @param tryCount 尝试次数，包含第一次发送\n\t * @return 当前对象\n\t */\n\tpublic Client tryCount(int tryCount) {\n\t\tthis.tryCount = tryCount > 0 ? tryCount : 1;\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置直连url不能用的时候，是否使用注册中心上的地址\n\t * \n\t * @param backup 失败时是否启用注册中心上的地址\n\t * @return 当前对象\n\t */\n\tpublic Client backup(boolean backup) {\n\t\tthis.backup = backup;\n\t\treturn this;\n\t}\n\n\tpublic Client timeout(int timeout) {\n\t\tthis.totalTimeout = timeout > 0 ? timeout : 1;\n\t\treturn this;\n\t}\n\n\tpublic Client callback(Consumer<RpcCallInfo> callback) {\n\t\tthis.callback = callback;\n\t\treturn this;\n\t}\n\n\tpublic Client paramInArray(Object... args) {\n\t\tif (args == null) {\n\t\t\targs = new String[0];\n\t\t}\n\t\tString[] params = new String[args.length];\n\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\tObject arg = args[i];\n\t\t\tif (arg == null) {\n\t\t\t\tparams[i] = null;\n\t\t\t} else if (arg.getClass() == String.class) {\n\t\t\t\tparams[i] = (String) arg;\n\t\t\t} else {\n\t\t\t\tparams[i] = RpcJson.client().toJson(arg);\n\t\t\t}\n\t\t}\n\t\tthis.params = params;\n\t\tthis.paramType = ParamType.JSONARRAY;\n\t\treturn this;\n\t}\n\n\tpublic Client paramInJson(String json) {\n\t\tthis.params = json;\n\t\tthis.paramType = ParamType.JSON;\n\t\treturn this;\n\t}\n\n\tpublic Client paramInMap(Map<String, ?> map) {\n\t\treturn paramInJson(RpcJson.client().toJson(map));\n\t}\n\n\tprotected Req createReq() {\n\t\tReq req = new Req();\n\t\tActionContext context = ActionContext.current();\n\t\tif (context.isTest()) {\n\t\t\treq.setTest(true);\n\t\t}\n\t\treq.setStart(System.currentTimeMillis());\n\t\tString sn = UUIDSeed.seq18();\n\t\treq.setFullSn(sn, context.traceId(), context.nextSpanId());\n\t\treq.setUserId(context.userId());\n\t\treq.setApi(this.api);\n\t\treq.setFrom(Rpc.appId());\n\n\t\treq.setAttachments(context.attachmentView());\n\t\treturn req;\n\t}\n\n\t/**\n\t * 本方法调用之后，不允许再调用本对象的任何方法<BR>\n\t * \n\t * @return 用无论是否成功，都会返回future。如果失败的话，异常包含在future中。<BR>\n\t *         通信异常是SoaException；如果是业务类异常，则是BizException\n\t */\n\tpublic RpcFuture execute() {\n\t\tObjects.requireNonNull(this.paramType, \"param have not been set\");\n\t\tReq req = this.createReq();\n\t\tlong endTime = req.getStart() + this.totalTimeout;\n\t\treq.setParams(this.paramType.protocol(), this.params);\n\t\tint count = this.tryCount;\n\t\twhile (true) {\n\t\t\tRpcFuture f = sendAsync(req, endTime);\n\t\t\tif (f.getClass() == ErrorRpcFuture.class) {\n\t\t\t\tErrorRpcFuture errorFuture = (ErrorRpcFuture) f;\n\t\t\t\tRpcLocker locker = errorFuture.locker;\n\t\t\t\tLockHolder.remove(locker.req.getSn());\n\t\t\t\tif (--count > 0 && errorFuture.rpcResult().exception().isSameCode(RpcErrorCode.SEND_FAILED)\n\t\t\t\t\t\t&& System.currentTimeMillis() + 5 < endTime) {\n\t\t\t\t\tlocker.discard(errorFuture.rpcResult());\n\t\t\t\t\tLogs.rpc().warn(\"无法发送数据到{}，重试rpc请求\", locker.url());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlocker.wakeupAndLog(errorFuture.rpcResult());\n\t\t\t}\n\t\t\treturn f;\n\t\t}\n\t}\n\n\tprivate Host selectDirectUrl() {\n\t\tint index = COUNTER.incrementAndGet();\n\t\tif (index < 0) {\n\t\t\tCOUNTER.set((int) (System.nanoTime() & 0xff));\n\t\t\tindex = COUNTER.incrementAndGet();\n\t\t}\n\t\tfor (int i = 0; i < this.directUrls.length; i++) {\n\t\t\tindex %= directUrls.length;\n\t\t\tHost url = this.directUrls[index];\n\t\t\tif (!HostChecker.get().isDowned(url)) {\n\t\t\t\treturn url;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate RpcFuture sendAsync(final Req req, final long endTime) {\n\t\tfinal RpcLocker locker = new RpcLocker(req, callback);\n\t\tHost url = null;\n\t\tif (this.directUrls != null && this.directUrls.length > 0) {\n\t\t\turl = selectDirectUrl();\n\t\t\tif (url == null && !this.backup) {\n\t\t\t\tSoaException ex = new SoaException(RpcErrorCode.NO_NODE_AVAILABLE,\n\t\t\t\t\t\t\"all directUrls is disabled:\" + Arrays.toString(this.directUrls), null);\n\t\t\t\treturn new ErrorRpcFuture(ex, locker);\n\t\t\t}\n\t\t}\n\t\tif (url == null) {\n\n\t\t\tRouter<Host> route = RpcRoutes.getRoute(api);\n\t\t\tRpcFuture future = this.tryLocalHandler(req, locker, route);\n\t\t\tif (future != null) {\n\t\t\t\treturn future;\n\t\t\t}\n\n\t\t\tif (route == null) {\n\t\t\t\tSoaException ex = new SoaException(RpcErrorCode.NO_ROUTE, \"can not find route for \" + api, null);\n\t\t\t\treturn new ErrorRpcFuture(ex, locker);\n\t\t\t}\n\t\t\turl = route.select();\n\t\t}\n\t\tif (url == null) {\n\t\t\tSoaException ex = new SoaException(RpcErrorCode.NO_NODE_AVAILABLE, \"route for \" + api + \" are all disabled\",\n\t\t\t\t\tnull);\n\t\t\treturn new ErrorRpcFuture(ex, locker);\n\t\t}\n\t\tlocker.url(url);\n\t\treq.setServerProtocol(RpcRoutes.getServerProtocol(url));\n\t\tRpcWriteFuture f = null;\n\t\ttry {\n\t\t\tTransportClient reqSession = TransportClientHolder.getSession(url);\n\t\t\tLockHolder.register(locker, endTime);\n\t\t\tf = reqSession.write(req);\n\t\t} catch (Exception e) {\n\t\t\tLogs.rpc().error(e.getLocalizedMessage(), e);\n\t\t}\n\t\tif (f == null) {\n\t\t\tSoaException ex = new SoaException(RpcErrorCode.SEND_FAILED, url + \" can not connect\", null);\n\t\t\treturn new ErrorRpcFuture(ex, locker);\n\t\t}\n\t\tf.addListener(locker);\n\t\treturn new RpcFutureImpl(locker);\n\t}\n\n\tprivate RpcFuture tryLocalHandler(Req req, RpcLocker locker, Router<Host> route) {\n\t\tRpcActionNode node = RpcActions.getActionNode(api);\n\t\tif (node == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (RpcSettings.disableLocalRoute() && route != null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tRequest request = new Request(req);\n\t\treq = null;\n\n\t\tActionContext context = ActionContext.current().clone();\n\t\ttry {\n\t\t\tInnerRpcUtil.rpcContext(request, context.isTest());\n\t\t\tlocker.url(LOCAL);\n\t\t\tResponse resp = LocalRequestHandler.inst.handler(request, node);\n\t\t\tActionContext.store(context);\n\t\t\tlocker.wakeupAndLog(new RpcResult(resp.json(), resp.exception()));\n\t\t} finally {\n\t\t\tActionContext.store(context);\n\t\t}\n\t\treturn new RpcFutureImpl(locker);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/ClientHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport org.yx.log.Log;\nimport org.yx.rpc.BusinessHandler;\nimport org.yx.rpc.server.Response;\nimport org.yx.rpc.transport.TransportChannel;\n\npublic class ClientHandler implements BusinessHandler {\n\n\t@Override\n\tpublic void received(TransportChannel channel, Object message) {\n\t\tif (message == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (message instanceof Response) {\n\t\t\tResponse resp = (Response) message;\n\t\t\tLockHolder.unLockAndSetResult(resp);\n\t\t\treturn;\n\t\t}\n\t\tLog.get(\"sumk.rpc.client\").warn(\"unkown client message type:{}\", message.getClass().getName());\n\t}\n\n\t@Override\n\tpublic void exceptionCaught(TransportChannel channel, Throwable exception) {\n\t\tLog.get(\"sumk.rpc.client\").error(channel + \" throw exception\", exception);\n\t\tchannel.closeNow();\n\t}\n\n\t@Override\n\tpublic void closed(TransportChannel channel) {\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/ErrorRpcFuture.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport org.yx.exception.CodeException;\nimport org.yx.exception.SoaException;\nimport org.yx.rpc.RpcErrorCode;\n\npublic class ErrorRpcFuture extends AbstractRpcFuture {\n\n\tprivate final RpcResult rpcResult;\n\n\tpublic ErrorRpcFuture(Throwable e, RpcLocker locker) {\n\t\tsuper(locker);\n\t\tCodeException exception = e instanceof CodeException ? (CodeException) e\n\t\t\t\t: SoaException.create(RpcErrorCode.UNKNOW, e.getMessage(), e);\n\t\tthis.rpcResult = new RpcResult(null, exception);\n\t}\n\n\tpublic RpcResult awaitForRpcResult() {\n\t\treturn this.rpcResult;\n\t}\n\n\t@Override\n\tpublic RpcResult rpcResult() {\n\t\treturn this.rpcResult;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/HostChecker.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport java.net.Socket;\nimport java.net.UnknownHostException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.TimeUnit;\n\nimport org.yx.common.Host;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\nimport org.yx.util.Task;\n\npublic class HostChecker {\n\n\tprivate static final HostChecker holder = new HostChecker();\n\n\tprivate HostChecker() {\n\t\tTask.scheduleAtFixedRate(new Checker(), 5, AppInfo.getInt(\"sumk.rpc.hosts.check.period\", 3), TimeUnit.SECONDS);\n\t}\n\n\tpublic static HostChecker get() {\n\t\treturn holder;\n\t}\n\n\tprivate final ConcurrentHashMap<Host, Long> downUrls = new ConcurrentHashMap<>();\n\n\tpublic boolean isDowned(Host url) {\n\t\treturn downUrls.containsKey(url);\n\t}\n\n\tpublic List<Host> available(List<Host> urls) {\n\t\tList<Host> us = new ArrayList<>(urls);\n\t\tList<Host> avas = new ArrayList<>(us.size());\n\t\tfor (Host u : us) {\n\t\t\tif (!downUrls.containsKey(u)) {\n\t\t\t\tavas.add(u);\n\t\t\t}\n\t\t}\n\t\treturn avas;\n\t}\n\n\tpublic void addDownUrl(Host url) {\n\t\tif (url == null) {\n\t\t\tLogs.rpc().warn(\"url is null\");\n\t\t\treturn;\n\t\t}\n\t\tdownUrls.putIfAbsent(url, System.currentTimeMillis());\n\t\tLogs.rpc().info(\"{} is down\", url);\n\t}\n\n\tprivate class Checker implements Runnable {\n\n\t\tprivate int getTimeOut(int urlSize) {\n\t\t\tif (urlSize == 1) {\n\t\t\t\treturn AppInfo.getInt(\"sumk.rpc.socket.connecttimeout.1\", 3000);\n\t\t\t} else if (urlSize == 2) {\n\t\t\t\treturn AppInfo.getInt(\"sumk.rpc.socket.connecttimeout.2\", 2500);\n\t\t\t} else if (urlSize > 5) {\n\t\t\t\treturn AppInfo.getInt(\"sumk.rpc.socket.connecttimeout.5\", 1000);\n\t\t\t}\n\t\t\treturn AppInfo.getInt(\"sumk.rpc.socket.connecttimeout.3\", 2000);\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif (downUrls.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tHost[] urls = downUrls.keySet().toArray(new Host[0]);\n\t\t\tint timeout = getTimeOut(urls.length);\n\t\t\tlong maxDownMilSecond = AppInfo.getLong(\"sumk.rpc.returnToAlive\", 1000L * 60 * 10);\n\t\t\tlong now = System.currentTimeMillis();\n\t\t\tfor (Host url : urls) {\n\t\t\t\tLong addTime = downUrls.get(url);\n\t\t\t\tif (addTime != null) {\n\t\t\t\t\tlong passedTime = now - addTime;\n\t\t\t\t\tif (passedTime > maxDownMilSecond) {\n\t\t\t\t\t\tdownUrls.remove(url);\n\t\t\t\t\t\tLogs.rpc().debug(\"{} remove from checker because it has been retried for {}ms\", url,\n\t\t\t\t\t\t\t\tpassedTime);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttry (Socket socket = new Socket()) {\n\n\t\t\t\t\tsocket.connect(url.toInetSocketAddress(), timeout);\n\t\t\t\t\tif (socket.isConnected()) {\n\t\t\t\t\t\tdownUrls.remove(url);\n\t\t\t\t\t\tLogs.rpc().info(\"{} reconected\", url);\n\t\t\t\t\t}\n\t\t\t\t} catch (UnknownHostException e) {\n\t\t\t\t\tLogs.rpc().error(e.getMessage(), e);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/LockHolder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.DelayQueue;\nimport java.util.concurrent.Delayed;\nimport java.util.concurrent.TimeUnit;\n\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.rpc.server.Response;\nimport org.yx.util.Task;\n\npublic final class LockHolder {\n\tprivate static final ConcurrentMap<String, RpcLocker> locks = new ConcurrentHashMap<>();\n\n\tstatic final LockTimeoutMonitor monitor = new LockTimeoutMonitor();\n\tstatic {\n\t\tTask.scheduleAtFixedRate(monitor, 1000, 500);\n\t}\n\n\tstatic void register(RpcLocker r, long endTime) {\n\t\tReq req = r.req;\n\t\tif (locks.putIfAbsent(req.getSn(), r) != null) {\n\n\t\t\tthrow new SumkException(111111111, req.getSn() + \" duplicate!!!!!!!!!!!!!!!!!!!!!\");\n\t\t}\n\t\tmonitor.add(req.getSn(), endTime);\n\t}\n\n\tstatic void unLockAndSetResult(Response resp) {\n\t\tRpcLocker r = locks.remove(resp.sn());\n\t\tif (r == null) {\n\t\t\tLog.get(\"sumk.rpc.client\").debug(\"{} has been removed.maybe is timeout.result:{}\", resp.sn(), resp.json());\n\t\t\treturn;\n\t\t}\n\t\tRpcResult result = new RpcResult(resp.json(), resp.exception());\n\t\tr.wakeupAndLog(result);\n\t}\n\n\tstatic RpcLocker remove(String sn) {\n\t\treturn locks.remove(sn);\n\t}\n\n\tstatic boolean containsKey(String sn) {\n\t\treturn locks.containsKey(sn);\n\t}\n\n\tpublic static int lockSize() {\n\t\treturn locks.size();\n\t}\n\n\tprivate static final class LockTimeoutMonitor implements Runnable {\n\n\t\tprivate static final DelayQueue<DelayedObject> QUEUE = new DelayQueue<>();\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\ttry {\n\t\t\t\tDelayedObject delay;\n\t\t\t\twhile ((delay = QUEUE.poll()) != null) {\n\t\t\t\t\tRpcLocker locker = LockHolder.remove(delay.sn);\n\t\t\t\t\tif (locker != null) {\n\t\t\t\t\t\tlocker.wakeupAndLog(RpcResult.timeout(locker.req));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.printStack(\"sumk.error\", e);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t}\n\n\t\tvoid add(String reqId, long endTime) {\n\t\t\tQUEUE.add(new DelayedObject(reqId, endTime));\n\t\t}\n\t}\n\n\tprivate static final class DelayedObject implements Delayed {\n\n\t\tprivate final long endTime;\n\t\tfinal String sn;\n\n\t\tpublic DelayedObject(String sn, long endTime) {\n\t\t\tthis.endTime = endTime;\n\t\t\tthis.sn = sn;\n\t\t}\n\n\t\t@Override\n\t\tpublic long getDelay(TimeUnit unit) {\n\t\t\treturn unit.convert(endTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(Delayed other) {\n\t\t\tlong d = other instanceof DelayedObject ? this.endTime - ((DelayedObject) other).endTime\n\t\t\t\t\t: this.getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS);\n\t\t\treturn (d == 0) ? 0 : ((d < 0) ? -1 : 1);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/ParamType.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport org.yx.rpc.codec.ReqParamType;\n\nenum ParamType {\n\tJSONARRAY(ReqParamType.REQ_PARAM_ORDER), JSON(ReqParamType.REQ_PARAM_JSON);\n\n\tprivate final int protocol;\n\n\tprivate ParamType(int p) {\n\t\tthis.protocol = p;\n\t}\n\n\tpublic int protocol() {\n\t\treturn this.protocol;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/Req.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\n\nimport org.yx.base.sumk.map.ListMap;\nimport org.yx.conf.Const;\nimport org.yx.rpc.codec.CodecKit;\nimport org.yx.rpc.codec.DataStream;\nimport org.yx.rpc.codec.Protocols;\nimport org.yx.rpc.codec.ReqParamType;\nimport org.yx.rpc.codec.StreamAble;\nimport org.yx.util.CollectionUtil;\n\npublic class Req implements StreamAble, Serializable {\n\n\tprivate static final long serialVersionUID = 123L;\n\n\tprivate String api;\n\n\tprivate String userId;\n\n\tprivate String sn;\n\n\tprivate String spanId;\n\n\tprivate String traceId;\n\n\tprivate Object params;\n\n\tprivate String from;\n\n\tprivate long startTime;\n\n\tprotected int protocol = Const.SUMK_VERSION;\n\n\tprivate int _serverProtocol;\n\n\tprivate Map<String, String> attachments;\n\n\tpublic void setFullSn(String sn, String traceId, String spanId) {\n\t\tthis.sn = Objects.requireNonNull(sn);\n\t\tthis.traceId = traceId;\n\t\tthis.spanId = Objects.requireNonNull(spanId);\n\t}\n\n\tpublic String getJsonedParam() {\n\t\treturn this.hasFeature(ReqParamType.REQ_PARAM_JSON) ? (String) params : null;\n\t}\n\n\tpublic void setParams(int type, Object params) {\n\t\tthis.setParamType(type & Protocols.REQUEST_PARAM_TYPES);\n\t\tthis.params = params;\n\t}\n\n\tprivate void setParamType(int type) {\n\t\tint p = this.protocol & (~Protocols.REQUEST_PARAM_TYPES);\n\t\tthis.protocol = p | type;\n\t}\n\n\tpublic Object getParams() {\n\t\treturn this.params;\n\t}\n\n\t/**\n\t * 数组方式传参才有，否则为null\n\t * \n\t * @return 如果参数是null,返回的也是null，如果是String类型，就是参数本身，否则是参数的json\n\t */\n\tpublic String[] getParamArray() {\n\t\treturn this.hasFeature(ReqParamType.REQ_PARAM_ORDER) ? (String[]) params : null;\n\t}\n\n\tpublic long getStart() {\n\t\treturn startTime;\n\t}\n\n\tpublic void setStart(long start) {\n\t\tthis.startTime = start;\n\t}\n\n\tpublic String getSn() {\n\t\treturn sn;\n\t}\n\n\tpublic String getTraceId() {\n\t\treturn this.traceId;\n\t}\n\n\tpublic String getSpanId() {\n\t\treturn this.spanId;\n\t}\n\n\tpublic String getApi() {\n\t\treturn api;\n\t}\n\n\tpublic void setApi(String api) {\n\t\tthis.api = api;\n\t}\n\n\tpublic String getFrom() {\n\t\treturn from;\n\t}\n\n\tpublic void setFrom(String src) {\n\t\tthis.from = src;\n\t}\n\n\tpublic void addFeature(int feature) {\n\t\tthis.protocol |= feature;\n\t}\n\n\tpublic boolean hasFeature(int feature) {\n\t\treturn Protocols.hasFeature(protocol, feature);\n\t}\n\n\tpublic int protocol() {\n\t\treturn this.protocol;\n\t}\n\n\tpublic void setTest(boolean b) {\n\t\taddFeature(Protocols.TEST);\n\t}\n\n\tpublic boolean isTest() {\n\t\treturn hasFeature(Protocols.TEST);\n\t}\n\n\tpublic String getUserId() {\n\t\treturn this.userId;\n\t}\n\n\tpublic void setUserId(String userId) {\n\t\tthis.userId = userId;\n\t}\n\n\tpublic Map<String, String> getAttachments() {\n\t\treturn attachments;\n\t}\n\n\tpublic void setAttachments(Map<String, String> attachments) {\n\t\tthis.attachments = attachments == null ? null : CollectionUtil.unmodifyMap(attachments);\n\t}\n\n\tpublic int getServerProtocol() {\n\t\treturn _serverProtocol;\n\t}\n\n\tpublic void setServerProtocol(int serverProtocol) {\n\t\tthis._serverProtocol = serverProtocol;\n\t}\n\n\t@Override\n\tpublic void writeTo(DataStream s) throws Exception {\n\t\ts.writeInt(8, 1);\n\t\ts.writePrefixedString(Integer.toString(this.protocol, Character.MAX_RADIX), 1);\n\t\ts.writePrefixedString(this.api, 1);\n\t\ts.writePrefixedString(this.userId, 1);\n\t\ts.writePrefixedString(this.sn, 1);\n\t\ts.writePrefixedString(this.traceId, 1);\n\t\ts.writePrefixedString(this.spanId, 1);\n\t\ts.writePrefixedString(this.from, 1);\n\t\ts.writePrefixedString(Long.toString(this.startTime, Character.MAX_RADIX), 1);\n\n\t\tint size = this.attachments == null ? 0 : this.attachments.size();\n\t\ts.writeInt(size, 1);\n\t\tif (size > 0) {\n\t\t\tfor (Entry<String, String> en : this.attachments.entrySet()) {\n\t\t\t\ts.writePrefixedString(en.getKey(), 1);\n\t\t\t\ts.writePrefixedString(en.getValue(), 2);\n\t\t\t}\n\t\t}\n\n\t\tif (this.hasFeature(ReqParamType.REQ_PARAM_JSON)) {\n\t\t\ts.writePrefixedString(this.getJsonedParam(), 4);\n\t\t} else {\n\t\t\tString[] ps = this.getParamArray();\n\t\t\ts.writeInt(ps.length, 1);\n\t\t\tfor (String p : ps) {\n\t\t\t\ts.writePrefixedString(p, 4);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void readFrom(DataStream s) throws Exception {\n\n\t\tint size = s.readInt(1);\n\t\tthis.protocol = Integer.parseInt(s.readPrefixedString(1), Character.MAX_RADIX);\n\t\tthis.api = s.readPrefixedString(1);\n\t\tthis.userId = s.readPrefixedString(1);\n\t\tthis.sn = s.readPrefixedString(1);\n\t\tthis.traceId = s.readPrefixedString(1);\n\t\tthis.spanId = s.readPrefixedString(1);\n\t\tthis.from = s.readPrefixedString(1);\n\t\tthis.startTime = Long.parseLong(s.readPrefixedString(1), Character.MAX_RADIX);\n\t\tCodecKit.skipPrefixedString(s, size - 8);\n\n\t\tsize = s.readInt(1);\n\t\tif (size > 0) {\n\t\t\tMap<String, String> map = new ListMap<>(size);\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\tmap.put(s.readPrefixedString(1), s.readPrefixedString(2));\n\t\t\t}\n\t\t\tthis.attachments = CollectionUtil.unmodifyMap(map);\n\t\t}\n\n\t\tif (this.hasFeature(ReqParamType.REQ_PARAM_JSON)) {\n\t\t\tthis.params = s.readPrefixedString(4);\n\t\t} else {\n\t\t\tsize = s.readInt(1);\n\t\t\tString[] ps = new String[size];\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\tps[i] = s.readPrefixedString(4);\n\t\t\t}\n\t\t\tthis.params = ps;\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic int getMessageType() {\n\t\treturn Protocols.REQUEST;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/Rpc.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.Executor;\n\nimport org.yx.bean.IOC;\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.main.SumkServer;\nimport org.yx.rpc.RpcSettings;\nimport org.yx.rpc.registry.RegistryFactory;\nimport org.yx.rpc.registry.client.RegistryClient;\nimport org.yx.rpc.transport.Transports;\nimport org.yx.util.SumkThreadPool;\nimport org.yx.util.Task;\n\npublic final class Rpc {\n\tprivate Rpc() {\n\t}\n\n\tprivate static volatile boolean strated;\n\n\tprivate static String appId;\n\n\tprivate static Optional<RegistryClient> registryClient;\n\n\tstatic String appId() {\n\t\treturn appId;\n\t}\n\n\tprivate static Executor clientExecutor = SumkThreadPool.executor();\n\n\tpublic static Executor clientExecutor() {\n\t\treturn clientExecutor;\n\t}\n\n\tpublic static void resetStatus() {\n\t\tstrated = false;\n\t}\n\n\tpublic static synchronized void init() {\n\t\tif (strated) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tappId = AppInfo.appId(\"sumk\");\n\t\t\tRpcSettings.init();\n\t\t\tRpc.clientExecutor = SumkServer.getExecutor(\"sumk.rpc.client.executor\");\n\t\t\tregistryClient = IOC.getFirstBean(RegistryFactory.class, false).registryClient();\n\t\t\tif (registryClient.isPresent()) {\n\t\t\t\tregistryClient.get().watch();\n\t\t\t}\n\t\t\tTransports.init();\n\t\t\tTransports.factory().initClient();\n\t\t\tTask.scheduleAtFixedRate(TransportClientHolder::cleanReqSession, 60_000, 60_000);\n\t\t\tstrated = true;\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\tpublic static Client create(String api) {\n\t\treturn new Client(api);\n\t}\n\n\t/**\n\t * 根据参数顺序调用rpc方法<BR>\n\t * 参数对象是原始的参数对象，不需要进行gson转化\n\t * \n\t * @param api  注册的接口名称，如a.b.c\n\t * @param args 支持泛型，比如List&lt;Integer&gt;之类的。但不提倡使用泛型。\n\t * @return json格式的服务器响应结果\n\t * @throws org.yx.exception.SoaException rpc异常\n\t * @throws org.yx.exception.BizException 业务异常\n\t */\n\tpublic static String call(String api, Object... args) {\n\t\treturn callAsync(api, args).getOrException();\n\t}\n\n\tpublic static String callInMap(String api, Map<String, ?> map) {\n\t\treturn callInMapAsync(api, map).getOrException();\n\t}\n\n\tprivate static <T> T fromJson(String json, Class<T> resultClz) {\n\t\tif (json == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn S.json().fromJson(json, resultClz);\n\t}\n\n\tpublic static <T> T invoke(Class<T> resultClz, String api, Object... args) {\n\t\treturn fromJson(call(api, args), resultClz);\n\t}\n\n\tpublic static <T> T invokeInMap(Class<T> resultClz, String api, Map<String, ?> map) {\n\t\treturn fromJson(callInMap(api, map), resultClz);\n\t}\n\n\t/**\n\t * 根据参数顺序<b>异步</b>调用rpc方法\n\t * \n\t * @param api  注册的接口名称，如a.b.c\n\t * @param args 支持泛型，比如List&lt;Integer&gt;之类的。但不提倡使用泛型\n\t * @return json格式的服务器响应结果\n\t */\n\tpublic static RpcFuture callAsync(String api, Object... args) {\n\t\treturn create(api).paramInArray(args).execute();\n\t}\n\n\tpublic static RpcFuture callInMapAsync(String api, Map<String, ?> map) {\n\t\treturn create(api).paramInMap(map).execute();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/RpcCallInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport org.yx.common.Host;\nimport org.yx.exception.CodeException;\n\npublic class RpcCallInfo {\n\tprivate final RpcResult result;\n\tprivate final Host server;\n\tprivate final String sn;\n\n\tpublic RpcCallInfo(String sn, RpcResult result, Host server) {\n\t\tthis.sn = sn;\n\t\tthis.result = result;\n\t\tthis.server = server;\n\t}\n\n\t/**\n\t * 返回值不为null\n\t * \n\t * @return 本次请求的唯一编码\n\t */\n\tpublic String getRequestId() {\n\t\treturn this.sn;\n\t}\n\n\t/**\n\t * 如果是本地调用，返回local:0\n\t * \n\t * @return 被调用的服务端地址\n\t */\n\tpublic Host getServer() {\n\t\treturn server;\n\t}\n\n\tpublic RpcResult getResult() {\n\t\treturn result;\n\t}\n\n\tpublic CodeException exception() {\n\t\treturn result == null ? null : result.exception;\n\t}\n\n\tpublic String json() {\n\t\treturn result == null ? null : result.json;\n\t}\n\n\tpublic <T> T optResult(Class<T> clz) {\n\t\treturn result == null ? null : result.optResult(clz);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/RpcFuture.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport org.yx.common.Host;\nimport org.yx.exception.CodeException;\n\npublic interface RpcFuture {\n\n\t/**\n\t * 等待返回，直到超时\n\t * \n\t * @return 如果没有异常，就返回信息，如果发生了异常，就抛出异常\n\t */\n\tString getOrException() throws CodeException;\n\n\t<T> T getOrException(Class<T> clz) throws CodeException;\n\n\t/**\n\t * 等待返回，直到超时\n\t * \n\t * @return 如果发生异常就返回null\n\t * \n\t */\n\tString opt();\n\n\t<T> T opt(Class<T> clz);\n\n\tRpcResult awaitForRpcResult();\n\n\t/**\n\t * 这个方法不会等待\n\t * \n\t * @return 如果已经收到返回值，就返回。否则返回null\n\t */\n\tRpcResult rpcResult();\n\n\tString getRequestId();\n\n\tHost getServer();\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/RpcFutureImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\npublic class RpcFutureImpl extends AbstractRpcFuture {\n\tprivate RpcResult result;\n\n\tpublic RpcFutureImpl(RpcLocker target) {\n\t\tsuper(target);\n\t}\n\n\t@Override\n\tpublic RpcResult awaitForRpcResult() {\n\t\tif (this.result != null) {\n\t\t\treturn this.result;\n\t\t}\n\t\tthis.result = locker.awaitForResponse();\n\t\treturn this.result;\n\t}\n\n\t@Override\n\tpublic RpcResult rpcResult() {\n\t\treturn this.result;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/RpcLocker.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport java.util.Objects;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.concurrent.locks.LockSupport;\nimport java.util.function.Consumer;\n\nimport org.yx.base.context.ActionContext;\nimport org.yx.base.context.LogContext;\nimport org.yx.common.Host;\nimport org.yx.exception.SoaException;\nimport org.yx.log.Logs;\nimport org.yx.rpc.RpcErrorCode;\nimport org.yx.rpc.log.RpcLog;\nimport org.yx.rpc.log.RpcLogs;\nimport org.yx.rpc.transport.RpcWriteFuture;\nimport org.yx.rpc.transport.RpcWriteListener;\n\npublic final class RpcLocker implements RpcWriteListener {\n\n\tprivate final AtomicReference<RpcResult> result = new AtomicReference<>();\n\n\tfinal Req req;\n\tprivate Host url;\n\tfinal Consumer<RpcCallInfo> callback;\n\tprivate final LogContext originLogContext;\n\n\tprivate final AtomicReference<Thread> awaitThread = new AtomicReference<>();\n\n\tRpcLocker(Req req, Consumer<RpcCallInfo> callback) {\n\t\tthis.req = Objects.requireNonNull(req);\n\t\tthis.callback = callback;\n\t\tthis.originLogContext = ActionContext.current().logContext();\n\t}\n\n\tpublic LogContext originLogContext() {\n\t\treturn this.originLogContext;\n\t}\n\n\tpublic void url(Host url) {\n\t\tthis.url = url;\n\t}\n\n\tpublic Host url() {\n\t\treturn url;\n\t}\n\n\tpublic boolean isWaked() {\n\t\treturn this.result.get() != null;\n\t}\n\n\tpublic void wakeupAndLog(RpcResult result) {\n\t\tthis.wakeup0(result, true);\n\t}\n\n\tpublic void discard(RpcResult result) {\n\t\tthis.wakeup0(result, false);\n\t}\n\n\tprivate void wakeup0(RpcResult result, boolean finish) {\n\t\tObjects.requireNonNull(result, \"result cannot be null\");\n\t\tif (this.isWaked()) {\n\t\t\treturn;\n\t\t}\n\t\tif (!this.result.compareAndSet(null, result)) {\n\t\t\treturn;\n\t\t}\n\t\tif (!finish) {\n\t\t\treturn;\n\t\t}\n\n\t\tlong receiveTime = System.currentTimeMillis();\n\t\tThread thread = awaitThread.getAndSet(null);\n\t\tif (thread != null) {\n\t\t\tLockSupport.unpark(thread);\n\t\t}\n\t\tRpcLogs.clientLog(new RpcLog(this.url, this.req, this.originLogContext, result, receiveTime));\n\t\tif (this.callback != null) {\n\t\t\tActionContext old = ActionContext.current().clone();\n\t\t\ttry {\n\t\t\t\tActionContext.store(ActionContext.newContext(this.originLogContext));\n\t\t\t\tRpcCallInfo info = new RpcCallInfo(this.req.getSn(), this.result.get(), this.url);\n\t\t\t\tcallback.accept(info);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tLogs.rpc().error(e.getLocalizedMessage(), e);\n\t\t\t} finally {\n\t\t\t\tActionContext.store(old);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void afterWrited(final RpcWriteFuture future) {\n\t\tif (future.getException() == null) {\n\t\t\treturn;\n\t\t}\n\t\tRpc.clientExecutor().execute(() -> {\n\t\t\tif (LockHolder.remove(req.getSn()) == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (url != null) {\n\t\t\t\tHostChecker.get().addDownUrl(url);\n\t\t\t}\n\t\t\twakeupAndLog(RpcResult.sendFailed(req, future.getException()));\n\t\t});\n\t}\n\n\tpublic RpcResult awaitForResponse() {\n\t\tRpcResult rpcResult = this.result.get();\n\t\tif (rpcResult != null) {\n\t\t\treturn rpcResult;\n\t\t}\n\t\tThread currentThread = Thread.currentThread();\n\t\tif (!awaitThread.compareAndSet(null, currentThread)) {\n\t\t\tthrow SoaException.create(RpcErrorCode.TIMEOUT, \"cannot await twice\", new TimeoutException());\n\t\t}\n\t\twhile (result.get() == null) {\n\n\t\t\tLockSupport.parkUntil(System.currentTimeMillis() + 10000);\n\t\t}\n\n\t\trpcResult = this.result.get();\n\t\tif (rpcResult == null) {\n\t\t\trpcResult = RpcResult.timeout(req);\n\t\t}\n\t\treturn rpcResult;\n\t}\n}"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/RpcResult.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport org.yx.common.util.S;\nimport org.yx.exception.BizException;\nimport org.yx.exception.CodeException;\nimport org.yx.exception.SoaException;\nimport org.yx.rpc.RpcErrorCode;\n\npublic final class RpcResult {\n\tstatic RpcResult timeout(Req req) {\n\t\tif (req == null) {\n\t\t\treturn new RpcResult(null, new SoaException(RpcErrorCode.TIMEOUT, \"服务处理超时\", \"req is null\"));\n\t\t}\n\t\tlong timeout = System.currentTimeMillis() - req.getStart();\n\t\tString msg = \"timeout in \" + timeout + \"ms,sn=\" + req.getSn();\n\t\tSoaException exception = new SoaException(RpcErrorCode.TIMEOUT, \"服务处理超时\", msg);\n\t\treturn new RpcResult(null, exception);\n\t}\n\n\tstatic RpcResult sendFailed(Req req, Throwable e) {\n\t\treturn new RpcResult(null, parseException(e));\n\t}\n\n\tstatic CodeException parseException(Throwable e) {\n\t\tif (e == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!(e instanceof CodeException)) {\n\t\t\treturn SoaException.create(RpcErrorCode.UNKNOW, e.getMessage(), e);\n\t\t}\n\t\tif (SoaException.class == e.getClass()) {\n\t\t\tSoaException ex = (SoaException) e;\n\t\t\tif (ex.isBizException()) {\n\t\t\t\treturn BizException.create(ex.getCode(), ex.getMessage());\n\t\t\t}\n\t\t}\n\t\treturn (CodeException) e;\n\t}\n\n\tfinal String json;\n\tfinal CodeException exception;\n\n\tpublic RpcResult(String json, CodeException exception) {\n\t\tthis.json = exception != null ? null : json;\n\t\tthis.exception = parseException(exception);\n\t}\n\n\tpublic CodeException exception() {\n\t\treturn exception;\n\t}\n\n\tpublic void throwIfException() throws CodeException {\n\t\tif (this.exception != null) {\n\t\t\tthrow exception;\n\t\t}\n\t}\n\n\tpublic String json() {\n\t\treturn json;\n\t}\n\n\tpublic <T> T optResult(Class<T> clz) {\n\t\tif (this.json == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn S.json().fromJson(json, clz);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/TransportClientHolder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.yx.common.Host;\nimport org.yx.log.Logs;\nimport org.yx.rpc.transport.TransportClient;\nimport org.yx.rpc.transport.Transports;\n\npublic class TransportClientHolder {\n\tprivate static ConcurrentMap<Host, TransportClient> sessions = new ConcurrentHashMap<>();\n\n\tpublic static void addClientIfAbsent(Host url, TransportClient s) {\n\t\tsessions.putIfAbsent(url, s);\n\t}\n\n\tpublic static boolean remove(Host url, TransportClient expect) {\n\t\treturn sessions.remove(url, expect);\n\t}\n\n\tpublic static TransportClient getSession(Host url) {\n\t\tTransportClient obj = sessions.get(url);\n\t\tif (obj != null) {\n\t\t\treturn obj;\n\t\t}\n\t\tTransportClient ses = Transports.factory().connect(url);\n\t\tTransportClient ses0 = sessions.putIfAbsent(url, ses);\n\t\tif (ses0 == null) {\n\t\t\treturn ses;\n\t\t}\n\t\tses.closeIfPossibble();\n\t\treturn ses0;\n\t}\n\n\tpublic static Map<Host, TransportClient> view() {\n\t\treturn Collections.unmodifiableMap(sessions);\n\t}\n\n\tpublic static synchronized void cleanReqSession() {\n\t\tLogs.rpc().debug(\"begin clean idle client transport\");\n\t\tMap<Host, TransportClient> map = sessions;\n\t\tfor (Host h : map.keySet()) {\n\t\t\tTransportClient session = map.get(h);\n\t\t\tif (session == null || !session.isIdle()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tLogs.rpc().info(\"remove idle client transport {}\", session);\n\n\t\t\tmap.remove(h, session);\n\t\t\tsession.closeIfPossibble();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/intf/IntfClientHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client.intf;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Type;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.yx.common.json.JsonTypes;\nimport org.yx.common.util.S;\nimport org.yx.rpc.client.Client;\nimport org.yx.rpc.client.Rpc;\nimport org.yx.rpc.spec.RpcSpecs;\nimport org.yx.rpc.spec.SoaClientConfigSpec;\nimport org.yx.util.ExceptionUtil;\n\npublic class IntfClientHandler implements InvocationHandler {\n\n\tprotected final String prefix;\n\tprotected final Map<String, SoaClientConfigSpec> map;\n\n\tpublic IntfClientHandler(String prefix, Class<?> intf) {\n\t\tthis.prefix = prefix;\n\t\tthis.map = buildClientMap(intf);\n\t}\n\n\tprotected Map<String, SoaClientConfigSpec> buildClientMap(Class<?> intf) {\n\t\tMap<String, SoaClientConfigSpec> tmp = new HashMap<>();\n\t\tMethod[] ms = intf.getMethods();\n\t\tfor (Method m : ms) {\n\t\t\tSoaClientConfigSpec sc = RpcSpecs.extractSoaClientConfig(m);\n\t\t\tif (sc != null) {\n\t\t\t\ttmp.put(m.getName(), sc);\n\t\t\t}\n\t\t}\n\t\treturn tmp.isEmpty() ? Collections.emptyMap() : tmp;\n\t}\n\n\t@Override\n\tpublic Object invoke(Object proxy, Method method, Object[] args) {\n\n\t\tif (\"toString\".equals(method.getName()) && (args == null || args.length == 0)) {\n\t\t\treturn this.getApi(method);\n\t\t}\n\t\ttry {\n\t\t\treturn this.onInvoke(proxy, method, args);\n\t\t} catch (Throwable e) {\n\t\t\tthrow ExceptionUtil.toRuntimeException(e);\n\t\t}\n\t}\n\n\tprotected String getApi(Method method) {\n\t\treturn prefix + method.getName();\n\t}\n\n\tprotected Object onInvoke(Object proxy, Method method, Object[] args) throws Exception {\n\t\tSoaClientConfigSpec sc = map.get(method.getName());\n\t\tClient client = Rpc.create(getApi(method));\n\t\tclient.paramInArray(args);\n\t\tif (sc != null) {\n\t\t\tif (sc.timeout() > 0) {\n\t\t\t\tclient.timeout(sc.timeout());\n\t\t\t}\n\t\t\tif (sc.tryCount() > 0) {\n\t\t\t\tclient.tryCount(sc.tryCount());\n\t\t\t}\n\t\t}\n\t\tString json = client.execute().getOrException();\n\t\tif (json == null || method.getReturnType() == Void.TYPE) {\n\t\t\treturn null;\n\t\t}\n\t\tString gen = method.getGenericReturnType().getTypeName();\n\t\tType type = JsonTypes.get(gen);\n\t\tif (type == null) {\n\t\t\treturn S.json().fromJson(json, method.getReturnType());\n\t\t}\n\t\treturn S.json().fromJson(json, type);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/intf/InvocationHandlerFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client.intf;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport org.yx.rpc.context.InnerRpcUtil;\n\npublic final class InvocationHandlerFactory {\n\tprivate static Function<Class<?>, InvocationHandler> factory = intf -> {\n\t\tString prefix = InnerRpcUtil.parseRpcIntfPrefix(intf);\n\t\treturn new IntfClientHandler(prefix, intf);\n\t};\n\n\tpublic static Function<Class<?>, InvocationHandler> getFactory() {\n\t\treturn factory;\n\t}\n\n\tpublic static void setFactory(Function<Class<?>, InvocationHandler> factory) {\n\t\tInvocationHandlerFactory.factory = Objects.requireNonNull(factory);\n\t}\n\n\tpublic static InvocationHandler create(Class<?> clz) {\n\t\treturn factory.apply(clz);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/intf/SoaClientFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client.intf;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Proxy;\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.slf4j.Logger;\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.Exclude;\nimport org.yx.annotation.Priority;\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.base.matcher.Matchers;\nimport org.yx.base.scaner.ClassScaner;\nimport org.yx.bean.FactoryBean;\nimport org.yx.bean.IOC;\nimport org.yx.bean.InterfaceBean;\nimport org.yx.common.json.JsonTypes;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\n@Priority(20000)\n@Bean\npublic class SoaClientFactory implements FactoryBean {\n\n\tprotected Set<String> getInterfaceNames() {\n\t\tPredicate<String> exclude = BooleanMatcher.FALSE;\n\t\tString patterns = AppInfo.getLatin(\"sumk.rpc.intfclient.exclude\", null);\n\t\tif (patterns != null) {\n\t\t\texclude = Matchers.createWildcardMatcher(patterns, 1);\n\t\t}\n\n\t\tString packs = AppInfo.getLatin(\"sumk.rpc.intfclient.package\", null);\n\t\tSet<String> clzNames = new HashSet<>();\n\t\tif (packs != null) {\n\t\t\tString[] ps = packs.split(Const.COMMA);\n\t\t\tCollection<String> temp = ClassScaner.listClasses(Arrays.asList(ps));\n\t\t\tfor (String c : temp) {\n\t\t\t\tif (exclude.test(c)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tclzNames.add(c);\n\t\t\t}\n\t\t}\n\t\tString interfaces = AppInfo.getLatin(\"sumk.rpc.intfclient.interface\", null);\n\t\tif (StringUtil.isNotEmpty(interfaces)) {\n\t\t\tfor (String s : interfaces.split(\",\")) {\n\t\t\t\ts = s.trim();\n\t\t\t\tif (s.length() > 0) {\n\t\t\t\t\tif (exclude.test(s)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tclzNames.add(s);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn clzNames;\n\t}\n\n\t@Override\n\tpublic Collection<Object> beans() {\n\n\t\tif (AppInfo.getBoolean(\"sumk.rpc.intfclient.disable\", false)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tLogger log = Logs.ioc();\n\t\tList<Object> ret = new ArrayList<>();\n\n\t\tSet<String> clzNames = this.getInterfaceNames();\n\t\tfor (String c : clzNames) {\n\t\t\ttry {\n\t\t\t\tClass<?> intf = Loader.loadClass(c);\n\t\t\t\tif (!intf.isInterface() || (intf.getModifiers() & Modifier.PUBLIC) == 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (intf.isAnnotationPresent(Exclude.class)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (IOC.get(intf) != null) {\n\t\t\t\t\tlog.debug(\"{}接口已经有对应的bean，类型为{},就不创建微服务客户端了\", intf.getName(), IOC.get(intf).getClass().getName());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tMethod[] ms = intf.getMethods();\n\t\t\t\tif (ms == null || ms.length == 0) {\n\t\t\t\t\tlog.debug(\"{}接口没有任何方法，被过滤掉\", c);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlog.debug(\"add soa client interface {}\", c);\n\t\t\t\tret.add(new InterfaceBean(intf, proxyInterface(intf)));\n\t\t\t\tregisteGenericReturnType(ms);\n\t\t\t} catch (NoClassDefFoundError e) {\n\t\t\t\tlog.warn(\"soa client interface {} ignored.{}\", c, e.getMessage());\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.printStack(\"sumk.error\", e);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\tprivate void registeGenericReturnType(Method[] ms) {\n\t\tfor (Method m : ms) {\n\t\t\tType genericReturnType = m.getGenericReturnType();\n\t\t\tif (genericReturnType instanceof ParameterizedType) {\n\t\t\t\tJsonTypes.registe(genericReturnType);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected Object proxyInterface(Class<?> intf) {\n\t\treturn Proxy.newProxyInstance(Loader.loader(), new Class[] { intf }, getInvocationHandler(intf));\n\t}\n\n\tprotected InvocationHandler getInvocationHandler(Class<?> intf) {\n\t\treturn InvocationHandlerFactory.create(intf);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/client/intf/SoaClientPlugin.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.client.intf;\n\nimport org.yx.annotation.Bean;\nimport org.yx.base.context.AppContext;\nimport org.yx.bean.Plugin;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\nimport org.yx.main.StartConstants;\nimport org.yx.rpc.client.Rpc;\n\n@Bean\npublic class SoaClientPlugin implements Plugin {\n\n\t@Override\n\tpublic void startAsync() {\n\t\tif (AppContext.inst().get(StartConstants.NOSOA_ClIENT) != null\n\t\t\t\t|| !AppInfo.getBoolean(\"sumk.rpc.client.start\", false)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tRpc.init();\n\t\t} catch (NoClassDefFoundError e) {\n\t\t\tLogs.ioc().warn(\"soa client donot start because some class not found: {}\", e.getLocalizedMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn 9990;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/AbstractDataBuffer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec;\n\nimport org.yx.rpc.transport.DataBuffer;\n\npublic abstract class AbstractDataBuffer implements DataBuffer {\n\n\t@Override\n\tpublic void writeBytes(byte[] bs) {\n\t\tthis.write(bs, 0, bs.length);\n\t}\n\n\t@Override\n\tpublic void writeInt(int x, int bytes) {\n\t\tswitch (bytes) {\n\t\tcase 1:\n\t\t\tif (x > 0xFF) {\n\t\t\t\tthrow new IllegalArgumentException(x + \"无法序列化到1个字节里\");\n\t\t\t}\n\t\t\tthis.writeBytes(new byte[] { (byte) x });\n\t\t\treturn;\n\t\tcase 2:\n\t\t\tif (x > 0xFFFF) {\n\t\t\t\tthrow new IllegalArgumentException(x + \"无法序列化到2个字节里\");\n\t\t\t}\n\t\t\tthis.writeBytes(new byte[] { (byte) (x >> 8), (byte) x });\n\t\t\treturn;\n\t\tcase 3:\n\t\t\tif (x > 0xFFFFFF) {\n\t\t\t\tthrow new IllegalArgumentException(x + \"无法序列化到3个字节里\");\n\t\t\t}\n\t\t\tthis.writeBytes(new byte[] { (byte) (x >> 16), (byte) (x >> 8), (byte) x });\n\t\t\treturn;\n\t\tcase 4:\n\t\t\tthis.writeBytes(new byte[] { (byte) (x >> 24), (byte) (x >> 16), (byte) (x >> 8), (byte) x });\n\t\t\treturn;\n\t\t}\n\t\tthrow new IllegalArgumentException(\"bytes必须在1-4之间\");\n\t}\n\n\t@Override\n\tpublic int readInt(int bytes) {\n\t\tif (bytes > 4 || bytes <= 0) {\n\t\t\tthrow new IllegalArgumentException(\"bytes必须在1-4之间\");\n\t\t}\n\t\tbyte[] bs = new byte[4];\n\t\tthis.read(bs, 4 - bytes, bytes);\n\t\treturn (bs[0] << 24) | ((bs[1] & 0xff) << 16) | ((bs[2] & 0xff) << 8) | ((bs[3] & 0xff));\n\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/CodecKit.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec;\n\nimport java.util.List;\n\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.rpc.codec.decoders.DataDecoder;\nimport org.yx.rpc.codec.encoders.DataEncoder;\nimport org.yx.rpc.transport.DataBuffer;\n\npublic final class CodecKit {\n\tpublic static final String LOG_NAME = \"sumk.rpc.codec\";\n\tprivate static DataEncoder[] encoders;\n\tprivate static DataDecoder[] decoders;\n\n\tpublic static void init(List<DataEncoder> encoders, List<DataDecoder> decoders) {\n\t\tCodecKit.encoders = encoders.toArray(new DataEncoder[encoders.size()]);\n\t\tCodecKit.decoders = decoders.toArray(new DataDecoder[decoders.size()]);\n\t}\n\n\tpublic static Object decode(DataBuffer in) throws Exception {\n\t\tif (!in.avilable(8)) {\n\t\t\treturn null;\n\t\t}\n\t\tfinal int begin = in.position();\n\t\tfinal int protocol = in.readInt(4);\n\t\tif ((protocol & 0xFF_00_00_00) != Protocols.MAGIC) {\n\t\t\tin.position(begin);\n\t\t\tthrow new SumkException(3432196, \"error magic,\" + Integer.toHexString(protocol));\n\t\t}\n\n\t\tint dataSize = in.readInt(4);\n\t\tif (!in.avilable(dataSize)) {\n\t\t\tin.position(begin);\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tfor (DataDecoder decoder : decoders) {\n\t\t\t\tif (decoder.accept(protocol)) {\n\t\t\t\t\treturn decoder.decode(protocol, in, dataSize);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new SumkException(394561243, \"no sumk decoder:\" + Integer.toHexString(protocol));\n\t\t} finally {\n\t\t\tint pos = in.position();\n\t\t\tint limit = begin + dataSize + 8;\n\t\t\tif (pos > begin && pos < limit) {\n\t\t\t\tLog.get(LOG_NAME).info(\"position forward {} -> {}\", pos, limit);\n\t\t\t\tin.position(limit);\n\t\t\t} else if (pos > limit) {\n\t\t\t\tLog.get(LOG_NAME).error(\"position turn back {} -> {}\", pos, limit);\n\t\t\t\tin.position(limit);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void encode(Object message, DataBuffer buf) throws Exception {\n\t\tfor (DataEncoder encoder : encoders) {\n\t\t\tif (encoder.encode(message, buf)) {\n\t\t\t\tbuf.flip();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthrow new SumkException(394561241, \"no sumk encoder for \" + message.getClass().getName());\n\t}\n\n\tpublic static void skipPrefixedString(DataStream s, int skipSize) throws Exception {\n\t\tif (skipSize <= 0) {\n\t\t\treturn;\n\t\t}\n\t\tfor (int i = 0; i < skipSize; i++) {\n\t\t\ts.readPrefixedString(1);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/DataStream.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec;\n\npublic interface DataStream {\n\t/**\n\t * 用于writePrefixedString和readPrefixedString做null的处理\n\t */\n\tpublic static final String NULL = new String(new char[] { 0x00, '\\n', 0x03, 'Y', 0x06, 0x00, 0x00, '6' });\n\n\tvoid position(int pos);\n\n\tint position();\n\n\tvoid writeInt(int k, int bytes);\n\n\t/**\n\t * 内部实现推荐用本接口的NULL常量来表示null，read的时候也要做响应的转义。 这个逻辑对使用者透明\n\t * \n\t * @param s           可以为null\n\t * @param lengthBytes 用于存储s序列化后的长度，只能是1 2 4其中的一个\n\t * @throws Exception 异常信息\n\t */\n\tvoid writePrefixedString(CharSequence s, int lengthBytes) throws Exception;\n\n\tvoid read(byte[] dst, int offset, int length);\n\n\tvoid write(byte[] src, int offset, int length);\n\n\tint readInt(int bytes);\n\n\tString readPrefixedString(int lengthBytes) throws Exception;\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/Protocols.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec;\n\nimport static org.yx.rpc.codec.ReqParamType.REQ_PARAM_JSON;\nimport static org.yx.rpc.codec.ReqParamType.REQ_PARAM_ORDER;\n\nimport org.yx.conf.Const;\n\npublic final class Protocols {\n\n\tpublic static final int MAGIC = 0x9A_00_00_00;\n\n\tpublic static final int REQUEST = 0x1_00_00;\n\n\tpublic static final int RESPONSE = 0x2_00_00;\n\n\tpublic static final int TEST = 0x10_00;\n\n\tpublic static final int REQUEST_PARAM_TYPES = REQ_PARAM_JSON | REQ_PARAM_ORDER;\n\n\tpublic static int profile() {\n\t\treturn REQUEST | RESPONSE | TEST | Const.SUMK_VERSION;\n\t}\n\n\tpublic static boolean hasFeature(int protocol, int feature) {\n\t\treturn (protocol & feature) == feature;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/ReqParamType.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec;\n\npublic interface ReqParamType {\n\n\tint REQ_PARAM_JSON = 0x01_00_00;\n\n\tint REQ_PARAM_ORDER = 0x02_00_00;\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/Request.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec;\n\nimport org.yx.rpc.client.Req;\n\npublic class Request extends Req {\n\n\tprivate static final long serialVersionUID = 123L;\n\n\t/**\n\t * 服务器端接收到req请求的时间\n\t */\n\tprivate long startInServer = System.currentTimeMillis();\n\n\tpublic long getStartInServer() {\n\t\treturn startInServer;\n\t}\n\n\tpublic Request() {\n\t}\n\n\tpublic Request(Req req) {\n\t\tthis.protocol = req.protocol();\n\t\tthis.setApi(req.getApi());\n\t\tthis.setUserId(req.getUserId());\n\t\tthis.setFullSn(req.getSn(), req.getTraceId(), req.getSpanId());\n\t\tthis.setFrom(req.getFrom());\n\t\tthis.setStart(req.getStart());\n\n\t\tthis.setAttachments(req.getAttachments());\n\t\tthis.setParams(req.protocol(), req.getParams());\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/StreamAble.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec;\n\npublic interface StreamAble {\n\tvoid writeTo(DataStream s) throws Exception;\n\n\tvoid readFrom(DataStream s) throws Exception;\n\n\tint getMessageType();\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/decoders/DataDecoder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec.decoders;\n\nimport org.yx.base.Ordered;\nimport org.yx.rpc.transport.DataBuffer;\n\npublic interface DataDecoder extends Ordered {\n\n\tboolean accept(int protocol);\n\n\tpublic Object decode(int protocol, DataBuffer data, int dataSize) throws Exception;\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/decoders/RequestDecoder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec.decoders;\n\nimport org.slf4j.Logger;\nimport org.yx.annotation.Bean;\nimport org.yx.log.Log;\nimport org.yx.rpc.codec.CodecKit;\nimport org.yx.rpc.codec.Protocols;\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.transport.DataBuffer;\n\n@Bean\npublic class RequestDecoder implements DataDecoder {\n\n\tprivate Logger log = Log.get(CodecKit.LOG_NAME);\n\n\t@Override\n\tpublic boolean accept(int protocol) {\n\t\treturn Protocols.hasFeature(protocol, Protocols.REQUEST);\n\t}\n\n\t@Override\n\tpublic Object decode(int protocol, DataBuffer data, int dataSize) throws Exception {\n\t\tif (log.isTraceEnabled()) {\n\t\t\tlog.trace(\"decode {} bytes to Request\", dataSize);\n\t\t}\n\t\tRequest req = new Request();\n\t\treq.readFrom(data);\n\t\treturn req;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/decoders/ResponseDecoder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec.decoders;\n\nimport org.slf4j.Logger;\nimport org.yx.annotation.Bean;\nimport org.yx.log.Log;\nimport org.yx.rpc.codec.CodecKit;\nimport org.yx.rpc.codec.Protocols;\nimport org.yx.rpc.server.Response;\nimport org.yx.rpc.transport.DataBuffer;\n\n@Bean\npublic class ResponseDecoder implements DataDecoder {\n\n\tprivate Logger log = Log.get(CodecKit.LOG_NAME);\n\n\t@Override\n\tpublic boolean accept(int protocol) {\n\t\treturn Protocols.hasFeature(protocol, Protocols.RESPONSE);\n\t}\n\n\t@Override\n\tpublic Object decode(int protocol, DataBuffer data, int dataSize) throws Exception {\n\t\tif (log.isTraceEnabled()) {\n\t\t\tlog.trace(\"decode {} bytes to Request\", dataSize);\n\t\t}\n\t\tResponse req = new Response();\n\t\treq.readFrom(data);\n\t\treturn req;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/encoders/AbstractEncoder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec.encoders;\n\nimport org.yx.conf.Const;\nimport org.yx.rpc.codec.Protocols;\nimport org.yx.rpc.transport.DataBuffer;\n\npublic abstract class AbstractEncoder<T> implements DataEncoder {\n\n\t@Override\n\tpublic final boolean encode(Object message, DataBuffer buffer) throws Exception {\n\t\tT obj = this.convert(message);\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tint start = buffer.position();\n\t\tbuffer.writeInt(Protocols.MAGIC | Const.SUMK_VERSION | this.getMessageType(obj), 4);\n\t\tbuffer.position(start + 8);\n\t\tthis.encodeBody(obj, buffer);\n\t\tthis.putLength(buffer, start + 4);\n\t\treturn true;\n\t}\n\n\tprotected abstract int getMessageType(T req);\n\n\tprotected abstract void encodeBody(T message, DataBuffer buffer) throws Exception;\n\n\tprotected abstract T convert(Object message);\n\n\tprivate void putLength(DataBuffer buffer, int start) {\n\t\tint limit = buffer.position();\n\t\tbuffer.position(start);\n\t\tbuffer.writeInt(limit - start - 4, 4);\n\t\tbuffer.position(limit);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/encoders/DataEncoder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec.encoders;\n\nimport org.yx.base.Ordered;\nimport org.yx.rpc.transport.DataBuffer;\n\npublic interface DataEncoder extends Ordered {\n\n\tboolean encode(Object message, DataBuffer buf) throws Exception;\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/codec/encoders/StreamAbleEncoder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.codec.encoders;\n\nimport org.yx.annotation.Bean;\nimport org.yx.rpc.codec.StreamAble;\nimport org.yx.rpc.transport.DataBuffer;\n\n@Bean\npublic class StreamAbleEncoder extends AbstractEncoder<StreamAble> {\n\n\t@Override\n\tpublic void encodeBody(StreamAble req, DataBuffer buffer) throws Exception {\n\t\treq.writeTo(buffer);\n\t}\n\n\t@Override\n\tprotected StreamAble convert(Object message) {\n\t\tif (message instanceof StreamAble) {\n\t\t\treturn (StreamAble) message;\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected int getMessageType(StreamAble req) {\n\t\treturn req.getMessageType();\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/context/InnerRpcUtil.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.context;\n\nimport org.yx.base.context.ActionContext;\nimport org.yx.conf.AppInfo;\nimport org.yx.rpc.client.Req;\nimport org.yx.util.StringUtil;\n\npublic final class InnerRpcUtil {\n\n\tpublic static ActionContext rpcContext(Req req, boolean isTest) {\n\t\tString traceId = StringUtil.isEmpty(req.getTraceId()) ? null : req.getTraceId();\n\t\treturn ActionContext.newContext(req.getApi(), traceId, req.getSpanId(), req.getUserId(), isTest,\n\t\t\t\treq.getAttachments());\n\t}\n\n\tpublic static String parseRpcIntfPrefix(Class<?> intfClz) {\n\n\t\treturn parseClassName2Prefix(intfClz.getName(), AppInfo.getInt(\"sumk.rpc.intf.name.partcount\", 3));\n\t}\n\n\tpublic static String parseClassName2Prefix(String name, int partCount) {\n\t\tString[] names = name.split(\"\\\\.\");\n\t\tif (names.length <= partCount) {\n\t\t\treturn name + \".\";\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(name.length());\n\t\tfor (int i = names.length - partCount; i < names.length; i++) {\n\t\t\tsb.append(StringUtil.uncapitalize(names[i])).append('.');\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/context/RpcActionNode.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.context;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Type;\n\nimport org.yx.bean.aop.asm.MethodPojo;\nimport org.yx.bean.aop.asm.ParamPojo;\nimport org.yx.bean.aop.context.CalleeNode;\nimport org.yx.exception.BizException;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.rpc.RpcErrorCode;\nimport org.yx.rpc.RpcJson;\nimport org.yx.rpc.codec.ReqParamType;\nimport org.yx.rpc.codec.Request;\n\npublic final class RpcActionNode extends CalleeNode {\n\tprivate final boolean publish;\n\n\tpublic RpcActionNode(Object obj, Method method, MethodPojo argClzInfo, int toplimit, boolean publish) {\n\t\tsuper(obj, method, argClzInfo, toplimit);\n\t\tthis.publish = publish;\n\t}\n\n\tpublic boolean publish() {\n\t\treturn this.publish;\n\t}\n\n\tpublic ParamPojo createJsonParamPojo(Request req) throws Throwable {\n\t\tif (this.params.paramLength() == 0) {\n\t\t\treturn this.createEmptyParamObj();\n\t\t}\n\t\tString args = req.getJsonedParam();\n\t\tParamPojo argObj = RpcJson.server().fromJson(args, this.params.paramClz());\n\t\treturn argObj == null ? this.createEmptyParamObj() : argObj;\n\t}\n\n\tpublic ParamPojo createOrderParamPojo(Request req) throws Throwable {\n\t\tint paramLength = params.paramLength();\n\t\tParamPojo pojo = this.createEmptyParamObj();\n\t\tif (paramLength == 0) {\n\t\t\treturn pojo;\n\t\t}\n\t\tString[] args = req.getParamArray();\n\t\tif (args == null) {\n\t\t\tif (req.hasFeature(ReqParamType.REQ_PARAM_ORDER)) {\n\t\t\t\tLogs.rpc().debug(\"{}需要传递{}个参数，实际却是null\", method.getName(), paramLength);\n\t\t\t\treturn pojo;\n\t\t\t}\n\t\t\tthrow new SumkException(12012, method.getName() + \"的参数类型不对，不是order类型\");\n\t\t}\n\t\tif (args.length != paramLength) {\n\t\t\tLogs.rpc().debug(\"{}需要传递{}个参数，实际传递{}个\", method.getName(), paramLength, args.length);\n\t\t}\n\n\t\tObject[] objs = new Object[paramLength];\n\t\tfor (int i = 0; i < paramLength; i++) {\n\t\t\tif (i >= args.length || args[i] == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tType paramType = this.params.getParamType(i);\n\t\t\tif (paramType == String.class) {\n\t\t\t\tobjs[i] = args[i];\n\t\t\t} else {\n\t\t\t\tobjs[i] = RpcJson.server().fromJson(args[i], paramType);\n\t\t\t}\n\t\t}\n\t\tpojo.setParams(objs);\n\t\treturn pojo;\n\t}\n\n\tpublic static void checkNode(String api, CalleeNode node) {\n\t\tif (node == null) {\n\t\t\tthrow new SumkException(123546, \"[\" + api + \"] is not found in this server\");\n\t\t}\n\t\tif (node.overflowThreshold()) {\n\t\t\tthrow BizException.create(RpcErrorCode.THREAD_THRESHOLD_OVER, \"微服务限流降级\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/context/RpcActions.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.context;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.yx.common.action.ActInfoUtil;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.main.SumkServer;\nimport org.yx.rpc.spec.RpcSpecs;\nimport org.yx.rpc.spec.SoaSpec;\nimport org.yx.util.StringUtil;\n\npublic final class RpcActions {\n\n\tprivate static Map<String, Class<?>> pojoMap = new ConcurrentHashMap<>();\n\n\tprivate static Map<String, RpcActionNode> actMap = new ConcurrentHashMap<>();\n\n\tpublic static Class<?> getArgType(String method) {\n\t\tString m = getArgClassName(method);\n\t\treturn pojoMap.get(m);\n\t}\n\n\tprivate static String getArgClassName(String method) {\n\t\tint k = method.lastIndexOf('.');\n\t\treturn method.substring(0, k) + \"_\" + method.substring(k + 1);\n\t}\n\n\tpublic static RpcActionNode getActionNode(String soaName) {\n\t\treturn actMap.get(soaName);\n\t}\n\n\tpublic static void putActNode(String soaName, RpcActionNode actInfo) {\n\t\tactMap.putIfAbsent(soaName, actInfo);\n\t}\n\n\tpublic static Set<String> soaSet() {\n\t\treturn actMap.keySet();\n\t}\n\n\tpublic static List<String> publishSoaSet() {\n\t\tList<String> list = new ArrayList<>(actMap.size());\n\t\tfor (Entry<String, RpcActionNode> entry : actMap.entrySet()) {\n\t\t\tString api = entry.getKey();\n\t\t\tif (needPublish(api, entry.getValue())) {\n\t\t\t\tlist.add(api);\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tstatic boolean needPublish(String api, RpcActionNode node) {\n\t\treturn AppInfo.getBoolean(\"sumk.rpc.publish.\".concat(api), node.publish());\n\t}\n\n\tpublic static List<Map<String, Object>> infos(boolean full) {\n\t\tif (!SumkServer.isRpcEnable()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<String> names = new ArrayList<>(actMap.keySet());\n\t\tList<Map<String, Object>> ret = new ArrayList<>(names.size());\n\t\tnames.sort(null);\n\t\tfor (String name : names) {\n\t\t\tRpcActionNode rpc = actMap.get(name);\n\t\t\tif (rpc == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tMap<String, Object> map = full ? ActInfoUtil.fullInfoMap(name, rpc) : ActInfoUtil.simpleInfoMap(name, rpc);\n\t\t\tret.add(map);\n\t\t\tSoaSpec soa = RpcSpecs.extractSoa(rpc.rawMethod());\n\t\t\tif (soa != null) {\n\t\t\t\tmap.put(\"cnName\", soa.cnName());\n\t\t\t\tif (StringUtil.isNotEmpty(rpc.comment())) {\n\t\t\t\t\tmap.put(\"comment\", rpc.comment());\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!needPublish(name, rpc)) {\n\t\t\t\tmap.put(\"publish\", Boolean.FALSE);\n\t\t\t}\n\t\t\tif (rpc.toplimit() != Const.DEFAULT_TOPLIMIT) {\n\t\t\t\tmap.put(\"toplimit\", rpc.toplimit());\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/data/ApiProfile.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.data;\n\nimport org.yx.util.StringUtil;\n\npublic class ApiProfile {\n\tprivate final String name;\n\tprivate Integer weight;\n\n\tpublic ApiProfile(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic Integer getWeight() {\n\t\treturn weight;\n\t}\n\n\tvoid setWeight(String w) {\n\t\tif (StringUtil.isEmpty(w)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.weight = Integer.valueOf(w);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn name;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/data/RouteDataOperator.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.data;\n\nimport java.util.Map;\n\nimport org.yx.common.Host;\n\npublic interface RouteDataOperator {\n\n\tString getName(Host host);\n\n\tbyte[] serialize(Host host, Map<String, String> data) throws Exception;\n\n\tRouteInfo deserialize(RoutePathData data) throws Exception;\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/data/RouteDataOperatorImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.data;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.slf4j.Logger;\nimport org.yx.common.Host;\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.rpc.registry.RegistryConst;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic class RouteDataOperatorImpl implements RouteDataOperator {\n\n\tprivate static final String SERVER = \"_server\";\n\tprivate Logger logger = Log.get(\"sumk.rpc.data\");\n\tprivate static final String SMALL_SPLIT = \"=\";\n\n\t@Override\n\tpublic RouteInfo deserialize(RoutePathData data) throws IOException {\n\t\tfinal String v = new String(data.data(), AppInfo.UTF8);\n\t\tMap<String, String> map = CollectionUtil.fillMapFromText(new HashMap<>(), v, AppInfo.LN, SMALL_SPLIT);\n\t\tMap<String, String> methodMap = CollectionUtil.subMap(map, RegistryConst.METHODS);\n\t\tif (methodMap.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tString s = map.get(SERVER);\n\t\tHost host = null;\n\t\tif (s == null || (host = Host.create(s)) == null) {\n\t\t\tlogger.error(\"{} 不是有效的host\", s);\n\t\t\treturn null;\n\t\t}\n\t\tRouteInfo info = new RouteInfo(host, data.name());\n\t\tString f = map.get(RegistryConst.FEATURE);\n\t\tif (StringUtil.isNotEmpty(f)) {\n\t\t\ttry {\n\t\t\t\tlong fv = Long.parseLong(f, 16);\n\t\t\t\tint reqProtocol = (int) fv;\n\t\t\t\tinfo.setFeature(reqProtocol);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLogs.rpc().info(f + \"不能解析为数字\", e);\n\t\t\t}\n\t\t}\n\t\tinfo.setWeight(map.get(RegistryConst.WEIGHT));\n\t\tList<ApiProfile> intfs = new ArrayList<>();\n\t\tfor (Entry<String, String> entry : methodMap.entrySet()) {\n\t\t\tString m = entry.getKey();\n\t\t\tString value = entry.getValue();\n\t\t\tif (m.length() == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tApiProfile intf = new ApiProfile(m);\n\t\t\tintfs.add(intf);\n\t\t\tif (value != null && value.length() > 0) {\n\t\t\t\tMap<String, String> methodProperties = CollectionUtil.loadMapFromText(value, \",\", \":\");\n\t\t\t\tintf.setWeight(methodProperties.get(RegistryConst.WEIGHT));\n\t\t\t}\n\t\t}\n\t\tinfo.setApis(intfs);\n\t\tif (logger.isTraceEnabled()) {\n\t\t\tlogger.trace(\"反序列化:  {}\\nzk上的数据: {}\\ninfo:  {}\", data.name(), v, S.json().toJson(info));\n\t\t}\n\t\treturn info;\n\t}\n\n\t@Override\n\tpublic byte[] serialize(Host host, Map<String, String> data) throws Exception {\n\t\tdata = new HashMap<>(data);\n\t\tdata.put(SERVER, host.toAddressString());\n\t\tString s = CollectionUtil.saveMapToText(data, AppInfo.LN, SMALL_SPLIT);\n\t\tif (logger.isTraceEnabled()) {\n\t\t\tlogger.trace(\"原始数据: {}\\n序列化后: {}\", data, s);\n\t\t}\n\t\treturn s.getBytes(AppInfo.UTF8);\n\t}\n\n\t@Override\n\tpublic String getName(Host host) {\n\t\treturn String.join(\"@\", AppInfo.appId(\"\"), host.toAddressString());\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/data/RouteDataOperators.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.data;\n\nimport java.util.Objects;\n\npublic final class RouteDataOperators {\n\tprivate static RouteDataOperator inst = new RouteDataOperatorImpl();\n\n\tpublic static RouteDataOperator inst() {\n\t\treturn inst;\n\t}\n\n\tpublic static void setOperator(RouteDataOperator op) {\n\t\tRouteDataOperators.inst = Objects.requireNonNull(op);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/data/RouteInfo.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.data;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.yx.common.Host;\nimport org.yx.util.CollectionUtil;\nimport org.yx.util.StringUtil;\n\npublic class RouteInfo {\n\tprivate Collection<ApiProfile> apis = Collections.emptyList();\n\tprivate final Host host;\n\tprivate final String path;\n\n\tprivate int weight;\n\tprivate int clientCount;\n\n\tprivate int feature;\n\n\tpublic RouteInfo(Host url, String path) {\n\t\tthis.host = url;\n\t\tthis.path = path;\n\t}\n\n\tpublic Collection<ApiProfile> apis() {\n\t\treturn apis;\n\t}\n\n\tvoid setApis(Collection<ApiProfile> list) {\n\t\tthis.apis = CollectionUtil.unmodifyList(list);\n\t}\n\n\tvoid setWeight(String w) {\n\t\tif (StringUtil.isEmpty(w)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.weight = Integer.parseInt(w);\n\t}\n\n\tvoid setClientCount(String w) {\n\t\tif (StringUtil.isEmpty(w)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.clientCount = Integer.parseInt(w);\n\t}\n\n\tvoid setFeature(int feature) {\n\t\tthis.feature = feature;\n\t}\n\n\tpublic int weight() {\n\t\treturn this.weight;\n\t}\n\n\tpublic int clientCount() {\n\t\treturn this.clientCount;\n\t}\n\n\tpublic Host host() {\n\t\treturn host;\n\t}\n\n\tpublic int feature() {\n\t\treturn feature;\n\t}\n\n\tpublic String path() {\n\t\treturn path;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/data/RoutePathData.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.data;\n\nimport org.yx.exception.SumkException;\n\npublic class RoutePathData {\n\tprivate String name;\n\tprivate byte[] data;\n\n\tpublic RoutePathData(String name, byte[] data) {\n\t\tif (name.contains(\"/\")) {\n\t\t\tthrow new SumkException(23543534, name + \"应该只是当前目录，而不是全路径\");\n\t\t}\n\t\tthis.name = name;\n\t\tthis.data = data;\n\t}\n\n\tpublic String name() {\n\t\treturn name;\n\t}\n\n\tpublic byte[] data() {\n\t\treturn data;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/log/PlainRpcLogHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.log;\n\nimport static org.yx.conf.AppInfo.LN;\n\nimport org.slf4j.Logger;\nimport org.yx.common.util.S;\nimport org.yx.exception.SoaException;\nimport org.yx.log.Log;\nimport org.yx.log.LogKits;\nimport org.yx.rpc.RpcSettings;\nimport org.yx.rpc.client.Req;\nimport org.yx.rpc.client.RpcResult;\nimport org.yx.rpc.codec.ReqParamType;\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.server.Response;\n\npublic class PlainRpcLogHandler implements RpcLogHandler {\n\n\tprotected boolean isLogEnable(Logger logger, long totalTime, Exception e) {\n\t\tif (e != null) {\n\n\t\t\treturn logger.isErrorEnabled();\n\t\t}\n\n\t\tif (logger.isDebugEnabled()) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn (logger.isInfoEnabled() && totalTime >= RpcSettings.infoTime())\n\t\t\t\t|| (logger.isWarnEnabled() && totalTime >= RpcSettings.warnTime());\n\t}\n\n\tprotected void appendParam(StringBuilder sb, Req req) {\n\t\tif (req.hasFeature(ReqParamType.REQ_PARAM_JSON)) {\n\t\t\tsb.append(\"   param(json): \").append(shortParam(req.getJsonedParam()));\n\t\t} else {\n\t\t\tsb.append(\"   param(array): \").append(shortParam(S.json().toJson(req.getParamArray())));\n\t\t}\n\t}\n\n\tprotected String shortParam(String data) {\n\t\treturn LogKits.shorterSubfix(data, RpcSettings.maxReqLogSize());\n\t}\n\n\tprotected String getResult(String json) {\n\t\treturn LogKits.shorterSubfix(json, RpcSettings.maxRespLogSize());\n\t}\n\n\t@Override\n\tpublic void clientLog(RpcLog rpcLog) {\n\t\tif (RpcSettings.isClientLogDisable() || rpcLog == null) {\n\t\t\treturn;\n\t\t}\n\t\tReq req = rpcLog.getReq();\n\t\tif (req == null) {\n\t\t\treturn;\n\t\t}\n\t\tString api = req.getApi();\n\t\tif (api == null || api.isEmpty() || api.startsWith(\"$\")) {\n\t\t\treturn;\n\t\t}\n\t\tLogger logger = Log.get(\"sumk.rpc.log.client\");\n\t\tlong totalTime = rpcLog.getReceiveTime() - req.getStart();\n\t\tRpcResult result = rpcLog.getResult();\n\t\tException e = result != null ? result.exception() : null;\n\t\tif (!this.isLogEnable(logger, totalTime, e)) {\n\t\t\treturn;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(64);\n\t\tif (req.getTraceId() != null) {\n\t\t\tsb.append('{').append(req.getTraceId());\n\t\t\tif (req.getSpanId() != null) {\n\t\t\t\tsb.append('-').append(req.getSpanId());\n\t\t\t}\n\t\t\tsb.append(\"}  \");\n\t\t}\n\t\tsb.append(api).append(\"  server:\").append(rpcLog.getServer()).append(\"  totalTime:\").append(totalTime)\n\t\t\t\t.append(LN);\n\t\tthis.appendParam(sb, req);\n\t\tif (result != null) {\n\t\t\tif (e == null) {\n\t\t\t\tsb.append(LN).append(\"   result: \").append(getResult(result.json()));\n\t\t\t}\n\t\t}\n\n\t\tif (e != null) {\n\t\t\tlogger.error(sb.toString(), e);\n\t\t} else if (totalTime >= RpcSettings.warnTime()) {\n\t\t\tlogger.warn(sb.toString());\n\t\t} else if (totalTime >= RpcSettings.infoTime()) {\n\t\t\tlogger.info(sb.toString());\n\t\t} else {\n\t\t\tlogger.debug(sb.toString());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void serverLog(Request req, Response resp) {\n\t\tif (RpcSettings.isServerLogDisable()) {\n\t\t\treturn;\n\t\t}\n\t\tLogger logger = Log.get(\"sumk.rpc.log.server\");\n\t\tlong totalTime = -1;\n\t\tSoaException e = null;\n\t\tif (resp != null) {\n\t\t\ttotalTime = resp.serviceInvokeMilTime();\n\t\t\te = resp.exception();\n\t\t}\n\t\tif (!this.isLogEnable(logger, totalTime, e)) {\n\t\t\treturn;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(64);\n\t\tif (req != null) {\n\t\t\tsb.append(req.getApi()).append(\"   serverTime:\").append(totalTime).append(LN);\n\t\t\tthis.appendParam(sb, req);\n\t\t}\n\t\tString json = resp != null ? resp.json() : null;\n\t\tif (e == null) {\n\t\t\tsb.append(LN).append(\"   result: \").append(getResult(json));\n\t\t}\n\n\t\tif (resp != null && resp.isSuccess()) {\n\t\t\tif (totalTime >= RpcSettings.warnTime()) {\n\t\t\t\tlogger.warn(sb.toString());\n\t\t\t} else if (totalTime > RpcSettings.infoTime()) {\n\t\t\t\tlogger.info(sb.toString());\n\t\t\t} else {\n\t\t\t\tlogger.debug(sb.toString());\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (e != null && e.isBizException()) {\n\t\t\tlogger.warn(sb.append(LN).append(e.toString()).toString());\n\t\t} else {\n\t\t\tlogger.error(sb.toString(), e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/log/RpcLog.java",
    "content": "package org.yx.rpc.log;\n\nimport org.yx.base.context.LogContext;\nimport org.yx.common.Host;\nimport org.yx.rpc.client.Req;\nimport org.yx.rpc.client.RpcResult;\n\n/**\n * 这里的属性，以及属性的子属性，都要只读，不要去修改它<BR>\n * Req里面有开始时间。<BR>\n * 全部时间都不为null\n * \n */\npublic class RpcLog {\n\tprivate final Host server;\n\tprivate final Req req;\n\tprivate final RpcResult result;\n\tprivate final LogContext originLogContext;\n\tprivate long receiveTime;\n\n\tpublic RpcLog(Host server, Req req, LogContext logContext, RpcResult result, long receiveTime) {\n\t\tthis.server = server;\n\t\tthis.req = req;\n\t\tthis.result = result;\n\t\tthis.receiveTime = receiveTime;\n\t\tthis.originLogContext = logContext;\n\t}\n\n\tpublic long getReceiveTime() {\n\t\treturn receiveTime;\n\t}\n\n\tpublic Host getServer() {\n\t\treturn server;\n\t}\n\n\tpublic Req getReq() {\n\t\treturn req;\n\t}\n\n\tpublic RpcResult getResult() {\n\t\treturn result;\n\t}\n\n\tpublic LogContext getOriginLogContext() {\n\t\treturn originLogContext;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RpcLog [server=\" + server + \", req=\" + req + \", result=\" + result + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/log/RpcLogHandler.java",
    "content": "package org.yx.rpc.log;\n\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.server.Response;\n\npublic interface RpcLogHandler {\n\n\tvoid clientLog(RpcLog log);\n\n\tvoid serverLog(Request req, Response resp);\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/log/RpcLogs.java",
    "content": "package org.yx.rpc.log;\n\nimport java.util.Objects;\n\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.server.Response;\n\npublic class RpcLogs {\n\n\tprivate static RpcLogHandler handler = new PlainRpcLogHandler();\n\n\tpublic static RpcLogHandler getHandler() {\n\t\treturn handler;\n\t}\n\n\tpublic static void setHandler(RpcLogHandler handler) {\n\t\tRpcLogs.handler = Objects.requireNonNull(handler);\n\t}\n\n\tpublic static void clientLog(RpcLog log) {\n\t\thandler.clientLog(log);\n\t}\n\n\tpublic static void serverLog(Request req, Response resp) {\n\t\thandler.serverLog(req, resp);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/monitor/RpcActionProvider.java",
    "content": "package org.yx.rpc.monitor;\n\nimport java.util.function.BiFunction;\n\npublic class RpcActionProvider implements BiFunction<String, String, Object> {\n\n\t@Override\n\tpublic Object apply(String t, String u) {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/monitor/RpcMonitor.java",
    "content": "package org.yx.rpc.monitor;\n\nimport static org.yx.common.monitor.Monitors.BLANK;\nimport static org.yx.conf.AppInfo.LN;\n\nimport org.yx.common.monitor.MessageProvider;\nimport org.yx.rpc.context.RpcActions;\nimport org.yx.rpc.data.RouteInfo;\nimport org.yx.rpc.registry.client.RpcRoutes;\nimport org.yx.util.SumkDate;\n\npublic class RpcMonitor implements MessageProvider {\n\n\tprivate String rpcData() {\n\t\tRpcRoutes route = RpcRoutes.current();\n\t\tStringBuilder sb = new StringBuilder(64).append(\"##rpcData\").append(BLANK)\n\t\t\t\t.append(SumkDate.of(route.createTime()));\n\t\tfor (RouteInfo info : route.zkDatas()) {\n\t\t\tsb.append(LN).append(info.path()).append(BLANK).append(info.host()).append(BLANK).append(info.apis());\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic Object get(String type, String key, Object param) {\n\t\tif (\"document\".equals(type)) {\n\t\t\tif (\"rpc-full\".equals(key)) {\n\t\t\t\treturn RpcActions.infos(true);\n\t\t\t}\n\t\t\tif (\"rpc-simple\".equals(key)) {\n\t\t\t\treturn RpcActions.infos(false);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tif (\"rpcData\".equals(key)) {\n\t\t\treturn rpcData();\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/DefaultChannelInitializerSupplier.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.netty;\n\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Supplier;\n\nimport org.slf4j.Logger;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Log;\nimport org.yx.rpc.BusinessHandler;\nimport org.yx.rpc.RpcSettings;\n\nimport io.netty.buffer.ByteBufAllocator;\nimport io.netty.channel.ChannelHandler;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.handler.timeout.IdleStateHandler;\n\npublic class DefaultChannelInitializerSupplier implements Supplier<ChannelInitializer<SocketChannel>> {\n\n\tprivate final ChannelHandler handler;\n\tprivate final boolean isServer;\n\n\tpublic DefaultChannelInitializerSupplier(BusinessHandler handler, boolean isServer) {\n\t\tthis.handler = new NettyHandler(handler);\n\t\tthis.isServer = isServer;\n\t}\n\n\t@Override\n\tpublic ChannelInitializer<SocketChannel> get() {\n\t\treturn new ChannelInitializer<SocketChannel>() {\n\n\t\t\t@Override\n\t\t\tprotected void initChannel(SocketChannel ch) throws Exception {\n\n\t\t\t\tch.pipeline()\n\t\t\t\t\t\t.addLast(new IdleStateHandler(\n\t\t\t\t\t\t\t\tisServer ? RpcSettings.maxServerIdleTime() : RpcSettings.maxClientIdleTime(),\n\t\t\t\t\t\t\t\tAppInfo.getLong(\"sumk.rpc.idle.write\", 0), 0, TimeUnit.MILLISECONDS))\n\t\t\t\t\t\t.addLast(new NettyEncoder()).addLast(new NettyDecoder()).addLast(handler);\n\t\t\t\tLogger log = Log.get(isServer ? \"sumk.rpc.server\" : \"sumk.rpc.client\");\n\t\t\t\tif (log.isDebugEnabled()) {\n\t\t\t\t\tByteBufAllocator alloc = ch.alloc();\n\t\t\t\t\tif (alloc != null) {\n\t\t\t\t\t\tlog.debug(\"netty channel alloc: {}\", alloc);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyChannel.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.netty;\n\nimport java.net.InetSocketAddress;\n\nimport org.yx.rpc.transport.RpcWriteFuture;\nimport org.yx.rpc.transport.TransportChannel;\n\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelFuture;\nimport io.netty.util.Attribute;\nimport io.netty.util.AttributeKey;\n\npublic class NettyChannel implements TransportChannel {\n\n\tprivate final Channel channel;\n\tprivate volatile boolean close;\n\n\tprivate NettyChannel(Channel channel) {\n\t\tthis.channel = channel;\n\t}\n\n\tpublic static NettyChannel create(Channel session) {\n\t\tAttribute<NettyChannel> attr = session.attr(AttributeKey.valueOf(TransportChannel.class.getName()));\n\t\tNettyChannel channel = attr.get();\n\t\tif (channel == null) {\n\t\t\tchannel = new NettyChannel(session);\n\t\t\tNettyChannel ch2 = attr.setIfAbsent(channel);\n\t\t\tif (ch2 != null) {\n\t\t\t\treturn ch2;\n\t\t\t}\n\t\t}\n\t\treturn channel;\n\t}\n\n\t@Override\n\tpublic InetSocketAddress getRemoteAddress() {\n\t\treturn (InetSocketAddress) channel.remoteAddress();\n\t}\n\n\t@Override\n\tpublic RpcWriteFuture write(Object message) {\n\t\tChannelFuture future = channel.writeAndFlush(message);\n\t\treturn new NettyWriteFuture(future);\n\t}\n\n\t@Override\n\tpublic boolean isConnected() {\n\t\treturn (!close) && channel.isActive();\n\t}\n\n\t@Override\n\tpublic boolean isClosing() {\n\t\tif (close) {\n\t\t\treturn true;\n\t\t}\n\t\treturn !channel.isActive() || !channel.isRegistered();\n\t}\n\n\t@Override\n\tpublic void closeNow() {\n\t\tclose = true;\n\t\tchannel.close();\n\t}\n\n\t@Override\n\tpublic void closeOnFlush() {\n\t\tthis.closeNow();\n\t}\n\n\t@Override\n\tpublic Object getAttribute(String key) {\n\t\tAttributeKey<Object> KEY = AttributeKey.valueOf(key);\n\t\tif (!channel.hasAttr(KEY)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn channel.attr(KEY).get();\n\t}\n\n\t@Override\n\tpublic void setAttribute(String key, Object value) {\n\t\tAttributeKey<Object> KEY = AttributeKey.valueOf(key);\n\t\tchannel.attr(KEY).set(value);\n\t}\n\n\t@Override\n\tpublic void removeAttribute(String key) {\n\t\tthis.setAttribute(key, null);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn String.valueOf(channel);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyClient.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.netty;\n\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Supplier;\n\nimport org.yx.common.Host;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.rpc.RpcSettings;\nimport org.yx.rpc.client.AbstractTransportClient;\nimport org.yx.rpc.client.ClientHandler;\nimport org.yx.rpc.transport.TransportClient;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelFuture;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.EventLoopGroup;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.channel.socket.nio.NioSocketChannel;\nimport io.netty.util.concurrent.DefaultThreadFactory;\n\npublic class NettyClient extends AbstractTransportClient {\n\n\tprivate static Supplier<Bootstrap> connectorSupplier = new BootstrapSupplier();\n\n\tpublic static void setConnectorSupplier(Supplier<Bootstrap> connectorSupplier) {\n\t\tNettyClient.connectorSupplier = Objects.requireNonNull(connectorSupplier);\n\t}\n\n\tpublic static Supplier<Bootstrap> connectorSupplier() {\n\t\treturn connectorSupplier;\n\t}\n\n\tpublic NettyClient(Host host) {\n\t\tsuper(host);\n\t}\n\n\tprivate void connect(Bootstrap connector, long timeout) throws InterruptedException {\n\n\t\tif (channel == null || channel.isClosing()) {\n\t\t\tLogs.rpc().debug(\"create session for {}\", addr);\n\t\t\tChannelFuture cf = connector.connect(addr.toInetSocketAddress());\n\n\t\t\tChannel se = null;\n\t\t\tif (cf.await(timeout)) {\n\t\t\t\tse = cf.channel();\n\t\t\t}\n\t\t\tif (se != null) {\n\t\t\t\tthis.channel = NettyChannel.create(se);\n\t\t\t\tthis.channel.setAttribute(TransportClient.class.getName(), this);\n\t\t\t\tLog.get(\"sumk.rpc.client\").info(\"built netty connetion: {}\", channel);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcf.cancel(true);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void connect() throws Exception {\n\t\tBootstrap connector = connectorSupplier.get();\n\t\tlong timeout = RpcSettings.clientDefaultTimeout() + 2000;\n\t\tif (lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {\n\t\t\ttry {\n\t\t\t\tif (channel != null && !channel.isClosing()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconnect(connector, timeout);\n\t\t\t} finally {\n\t\t\t\tlock.unlock();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class BootstrapSupplier implements Supplier<Bootstrap> {\n\n\t\tprivate volatile Bootstrap connector;\n\t\tprivate EventLoopGroup workerGroup;\n\t\tprivate Supplier<ChannelInitializer<SocketChannel>> channelInitializerSupplier;\n\n\t\tpublic BootstrapSupplier() {\n\t\t\tthis.channelInitializerSupplier = new DefaultChannelInitializerSupplier(new ClientHandler(), false);\n\t\t\tthis.workerGroup = new NioEventLoopGroup(AppInfo.getInt(\"sumk.rpc.client.worker.count\", 4),\n\t\t\t\t\tnew DefaultThreadFactory(\"NettyClientWorker\", true));\n\t\t}\n\n\t\t@Override\n\t\tpublic Bootstrap get() {\n\t\t\tBootstrap con = this.connector;\n\t\t\tif (con != null) {\n\t\t\t\treturn con;\n\t\t\t}\n\t\t\treturn this.create();\n\t\t}\n\n\t\tprivate synchronized Bootstrap create() {\n\t\t\tif (this.connector != null) {\n\t\t\t\treturn this.connector;\n\t\t\t}\n\t\t\tBootstrap bootstrap = new Bootstrap();\n\n\t\t\tbootstrap.group(workerGroup).channel(NioSocketChannel.class);\n\t\t\tNettyKit.configClient(bootstrap);\n\t\t\tbootstrap.handler(this.channelInitializerSupplier.get());\n\t\t\tthis.connector = bootstrap;\n\t\t\treturn bootstrap;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyConfigSetter.java",
    "content": "package org.yx.rpc.netty;\n\nimport org.yx.base.Ordered;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.bootstrap.ServerBootstrap;\n\npublic interface NettyConfigSetter extends Ordered {\n\tvoid configServer(ServerBootstrap b);\n\n\tvoid configClient(Bootstrap b);\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyDataBuffer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.netty;\n\nimport java.nio.charset.StandardCharsets;\n\nimport org.yx.exception.SumkException;\nimport org.yx.rpc.codec.AbstractDataBuffer;\nimport org.yx.rpc.codec.DataStream;\n\nimport io.netty.buffer.ByteBuf;\n\npublic class NettyDataBuffer extends AbstractDataBuffer {\n\n\tpublic static final int READ = 0;\n\tpublic static final int WRITE = 1;\n\tprivate final ByteBuf buf;\n\tprivate int mode;\n\n\tpublic NettyDataBuffer(ByteBuf buf, int mode) {\n\t\tthis.buf = buf;\n\t\tthis.mode = mode;\n\t}\n\n\t@Override\n\tpublic void flip() {\n\t\tif (mode == READ) {\n\t\t\tthis.mode = WRITE;\n\t\t} else {\n\t\t\tthis.mode = READ;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void position(int pos) {\n\t\tif (mode == READ) {\n\t\t\tbuf.readerIndex(pos);\n\t\t} else {\n\t\t\tbuf.writerIndex(pos);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int position() {\n\t\treturn mode == READ ? buf.readerIndex() : buf.writerIndex();\n\t}\n\n\tprivate void checkMode(int expect) {\n\t\tif (mode != expect) {\n\t\t\tthrow new SumkException(35435, \"mode要为\" + expect + \"才正确\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void writePrefixedString(CharSequence s, int lengthBytes) throws Exception {\n\t\tthis.checkMode(WRITE);\n\t\tif (s == null) {\n\t\t\ts = NULL;\n\t\t}\n\t\tint pos = buf.writerIndex();\n\t\tbuf.writerIndex(pos + lengthBytes);\n\t\tbuf.writeCharSequence(s, StandardCharsets.UTF_8);\n\t\tint last = buf.writerIndex();\n\t\tint len = last - pos - lengthBytes;\n\t\tthis.buf.writerIndex(pos);\n\t\tthis.writeInt(len, lengthBytes);\n\t\tthis.buf.writerIndex(last);\n\t}\n\n\t@Override\n\tpublic void read(byte[] dst, int offset, int length) {\n\t\tthis.checkMode(READ);\n\t\tbuf.readBytes(dst, offset, length);\n\t}\n\n\t@Override\n\tpublic void write(byte[] src, int offset, int length) {\n\t\tthis.checkMode(WRITE);\n\t\tbuf.writeBytes(src, offset, length);\n\t}\n\n\t@Override\n\tpublic String readPrefixedString(int lengthBytes) throws Exception {\n\t\tthis.checkMode(READ);\n\t\tint len = this.readInt(lengthBytes);\n\t\tString s = buf.readCharSequence(len, StandardCharsets.UTF_8).toString();\n\t\tif (DataStream.NULL.equals(s)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn s;\n\t}\n\n\t@Override\n\tpublic boolean avilable(int length) {\n\t\tif (this.mode == READ) {\n\t\t\treturn buf.isReadable(length);\n\t\t}\n\t\treturn buf.isWritable(length);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyDecoder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.netty;\n\nimport java.util.List;\n\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.rpc.codec.CodecKit;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.handler.codec.ByteToMessageDecoder;\n\npublic class NettyDecoder extends ByteToMessageDecoder {\n\n\t@Override\n\tprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {\n\t\ttry {\n\n\t\t\tObject message = CodecKit.decode(new NettyDataBuffer(in, NettyDataBuffer.READ));\n\t\t\tif (message == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tout.add(message);\n\t\t} catch (Exception e) {\n\t\t\tLogs.rpc().error(\"数据解析出错\", e);\n\t\t\tthrow new SumkException(346456319, \"数据解析出错,\" + e.getMessage());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyEncoder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.netty;\n\nimport org.yx.rpc.codec.CodecKit;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.handler.codec.MessageToByteEncoder;\nimport io.netty.util.internal.PlatformDependent;\n\npublic class NettyEncoder extends MessageToByteEncoder<Object> {\n\n\tpublic NettyEncoder() {\n\t\tsuper(PlatformDependent.directBufferPreferred());\n\t}\n\n\t@Override\n\tprotected void encode(ChannelHandlerContext ctx, Object message, ByteBuf out) throws Exception {\n\t\tif (message == null) {\n\t\t\treturn;\n\t\t}\n\t\tNettyDataBuffer buf = new NettyDataBuffer(out, NettyDataBuffer.WRITE);\n\t\tCodecKit.encode(message, buf);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.netty;\n\nimport org.yx.log.Logs;\nimport org.yx.rpc.BusinessHandler;\n\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelInboundHandler;\nimport io.netty.channel.socket.ChannelInputShutdownEvent;\nimport io.netty.handler.timeout.IdleStateEvent;\n\npublic class NettyHandler implements ChannelInboundHandler {\n\n\tprivate final BusinessHandler handler;\n\n\tpublic NettyHandler(BusinessHandler handler) {\n\t\tthis.handler = handler;\n\t}\n\n\t@Override\n\tpublic void channelActive(ChannelHandlerContext ctx) throws Exception {\n\t\tLogs.rpc().info(\"{} : channelActive\", ctx);\n\t}\n\n\t@Override\n\tpublic void channelInactive(ChannelHandlerContext ctx) throws Exception {\n\t\tthis.handler.closed(NettyChannel.create(ctx.channel()));\n\t}\n\n\t@Override\n\tpublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {\n\t\tthis.handler.received(NettyChannel.create(ctx.channel()), msg);\n\t}\n\n\t@Override\n\tpublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {\n\t}\n\n\t@Override\n\tpublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {\n\n\t\tif (evt instanceof IdleStateEvent) {\n\t\t\tLogs.rpc().info(\"{} : will close because of idle\", ctx);\n\t\t\tctx.close();\n\t\t\treturn;\n\t\t}\n\n\t\tif (evt instanceof ChannelInputShutdownEvent) {\n\t\t\tLogs.rpc().info(\"{} : evict ChannelInputShutdownEvent\", ctx);\n\t\t\tctx.close();\n\t\t\treturn;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {\n\t\tthis.handler.exceptionCaught(NettyChannel.create(ctx.channel()), cause);\n\t}\n\n\t@Override\n\tpublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {\n\t\tLogs.rpc().debug(\"{} : handlerAdded\", ctx);\n\t}\n\n\t@Override\n\tpublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {\n\t\tLogs.rpc().debug(\"{} : handlerRemoved\", ctx);\n\t}\n\n\t@Override\n\tpublic void channelRegistered(ChannelHandlerContext ctx) throws Exception {\n\t\tLogs.rpc().debug(\"{} : channelRegistered\", ctx);\n\t}\n\n\t@Override\n\tpublic void channelUnregistered(ChannelHandlerContext ctx) throws Exception {\n\t\tLogs.rpc().debug(\"{} : channelUnregistered\", ctx);\n\t}\n\n\t@Override\n\tpublic void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {\n\t\tLogs.rpc().debug(\"{} channelWritabilityChanged\", ctx);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyKit.java",
    "content": "package org.yx.rpc.netty;\n\nimport org.yx.bean.IOC;\nimport org.yx.conf.AppInfo;\nimport org.yx.rpc.RpcSettings;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.buffer.ByteBufAllocator;\nimport io.netty.buffer.PooledByteBufAllocator;\nimport io.netty.buffer.UnpooledByteBufAllocator;\nimport io.netty.channel.ChannelOption;\n\npublic final class NettyKit {\n\tpublic static void configServer(ServerBootstrap b) {\n\t\tb.option(ChannelOption.SO_BACKLOG, AppInfo.getInt(\"sumk.rpc.backlog\", 128))\n\t\t\t\t.option(ChannelOption.ALLOCATOR, new UnpooledByteBufAllocator(false))\n\t\t\t\t.childOption(ChannelOption.ALLOCATOR, getByteBufAllocator())\n\t\t\t\t.childOption(ChannelOption.TCP_NODELAY, AppInfo.getBoolean(\"sumk.rpc.tcp.nodelay\", true))\n\t\t\t\t.childOption(ChannelOption.SO_REUSEADDR, AppInfo.getBoolean(\"sumk.rpc.port.reuse\", false));\n\t\tNettyConfigSetter setter = IOC.getFirstBean(NettyConfigSetter.class, true);\n\t\tif (setter != null) {\n\t\t\tsetter.configServer(b);\n\t\t}\n\t}\n\n\tpublic static void configClient(Bootstrap b) {\n\t\tb.option(ChannelOption.SO_KEEPALIVE, AppInfo.getBoolean(\"sumk.rpc.keepalive\", true))\n\t\t\t\t.option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.ALLOCATOR, getByteBufAllocator())\n\t\t\t\t.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, RpcSettings.clientDefaultTimeout());\n\t\tNettyConfigSetter setter = IOC.getFirstBean(NettyConfigSetter.class, true);\n\t\tif (setter != null) {\n\t\t\tsetter.configClient(b);\n\t\t}\n\t}\n\n\tprivate static ByteBufAllocator getByteBufAllocator() {\n\n\t\tif (AppInfo.getBoolean(\"sumk.rpc.memory.pool\", true)) {\n\t\t\treturn PooledByteBufAllocator.DEFAULT;\n\t\t}\n\t\treturn UnpooledByteBufAllocator.DEFAULT;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.netty;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.util.List;\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.concurrent.locks.LockSupport;\nimport java.util.function.Supplier;\n\nimport org.yx.bean.IOC;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.rpc.server.RequestHandler;\nimport org.yx.rpc.server.ServerHandler;\nimport org.yx.rpc.transport.TransportServer;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.ChannelFuture;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.EventLoopGroup;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.channel.socket.nio.NioServerSocketChannel;\nimport io.netty.util.concurrent.DefaultThreadFactory;\n\npublic class NettyServer implements TransportServer {\n\n\tprivate final String host;\n\tprivate int port;\n\tprivate final Supplier<ChannelInitializer<SocketChannel>> channelInitSupplier;\n\tprivate ChannelFuture future;\n\n\tpublic NettyServer(String host, int port, List<RequestHandler> handlers) {\n\t\tthis.port = port;\n\t\tthis.host = host;\n\t\tthis.channelInitSupplier = createChannelInitializer(handlers);\n\t}\n\n\tprotected Supplier<ChannelInitializer<SocketChannel>> createChannelInitializer(List<RequestHandler> handlers) {\n\t\treturn new DefaultChannelInitializerSupplier(new ServerHandler(IOC.getBeans(RequestHandler.class)), true);\n\t}\n\n\tprotected InetSocketAddress listenAddr(boolean randomPort) {\n\t\tif (randomPort) {\n\t\t\tint start = AppInfo.getInt(\"sumk.rpc.port.start\", 10000);\n\t\t\tint end = AppInfo.getInt(\"sumk.rpc.port.end\", 60000);\n\t\t\tport = start + ThreadLocalRandom.current().nextInt(end - start);\n\t\t}\n\t\tif (host == null || host.trim().length() == 0) {\n\t\t\treturn new InetSocketAddress(port);\n\t\t}\n\t\treturn new InetSocketAddress(host, port);\n\t}\n\n\tpublic synchronized void start() {\n\t\tif (future != null) {\n\t\t\tLogs.rpc().info(\"server已经启动，绑定到{}端口\", port);\n\t\t\treturn;\n\t\t}\n\n\t\tServerBootstrap b = new ServerBootstrap();\n\t\ttry {\n\t\t\tEventLoopGroup bossGroup = new NioEventLoopGroup(AppInfo.getInt(\"sumk.rpc.server.boss.count\", 1),\n\t\t\t\t\tnew DefaultThreadFactory(\"NettyServerBoss\", true));\n\n\t\t\tEventLoopGroup workerGroup = new NioEventLoopGroup(AppInfo.getInt(\"sumk.rpc.server.worker.count\", 4),\n\t\t\t\t\tnew DefaultThreadFactory(\"NettyServerWorker\", true));\n\n\t\t\tb.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)\n\t\t\t\t\t.childHandler(channelInitSupplier.get());\n\t\t\tNettyKit.configServer(b);\n\t\t\tboolean randomPort = this.port < 1;\n\t\t\tfor (int i = 0; i < 50; i++) {\n\t\t\t\ttry {\n\t\t\t\t\tInetSocketAddress addr = listenAddr(randomPort);\n\t\t\t\t\tChannelFuture f = b.bind(addr).sync();\n\t\t\t\t\tLog.get(\"sumk.rpc.server\").info(\"rpc(netty) listening on \" + addr);\n\t\t\t\t\tthis.future = f;\n\t\t\t\t\tthis.port = addr.getPort();\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tif (randomPort) {\n\t\t\t\t\t\tLog.get(\"sumk.rpc.server\").info(\"{} was occupied,try another port...\", this.port);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tLog.get(\"sumk.rpc.server\").info(\"waiting for listening to {}: {}\", port, e);\n\t\t\t\t\tint time = AppInfo.getInt(\"sumk.rpc.server.starting.sleep\", 5000);\n\t\t\t\t\tLockSupport.parkUntil(System.currentTimeMillis() + time);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Throwable e) {\n\t\t\tLog.get(\"sumk.rpc.server\").error(e.getLocalizedMessage(), e);\n\t\t}\n\t\tif (this.future == null) {\n\t\t\tthrow new SumkException(38057306, \"start netty server failed\");\n\t\t}\n\t}\n\n\tpublic int getPort() {\n\t\treturn port;\n\t}\n\n\tpublic void stop() throws IOException {\n\t\tif (this.future == null) {\n\t\t\treturn;\n\t\t}\n\t\tfuture.channel().close();\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyTransportFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.netty;\n\nimport java.util.Map;\n\nimport org.yx.annotation.Bean;\nimport org.yx.base.Ordered;\nimport org.yx.bean.IOC;\nimport org.yx.common.Host;\nimport org.yx.conf.AppInfo;\nimport org.yx.rpc.server.RequestHandler;\nimport org.yx.rpc.transport.TransportClient;\nimport org.yx.rpc.transport.TransportFactory;\nimport org.yx.rpc.transport.TransportServer;\n\n@Bean\npublic class NettyTransportFactory implements TransportFactory {\n\n\tpublic NettyTransportFactory() {\n\t\tString prefix = \"io.netty.\";\n\t\tMap<String, String> map = AppInfo.subMap(prefix);\n\t\tfor (String key : map.keySet()) {\n\t\t\tSystem.setProperty(prefix + key, map.get(key));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initClient() {\n\t\tNettyClient.connectorSupplier().get();\n\t}\n\n\t@Override\n\tpublic TransportClient connect(Host serverAddr) {\n\t\treturn new NettyClient(serverAddr);\n\t}\n\n\t@Override\n\tpublic TransportServer bind(String ip, int port) {\n\t\treturn new NettyServer(ip, port, IOC.getBeans(RequestHandler.class));\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn Ordered.DEFAULT_ORDER + 1000;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/netty/NettyWriteFuture.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.netty;\n\nimport java.util.Objects;\n\nimport org.yx.rpc.transport.RpcWriteFuture;\nimport org.yx.rpc.transport.RpcWriteListener;\n\nimport io.netty.channel.ChannelFuture;\nimport io.netty.util.concurrent.GenericFutureListener;\n\npublic class NettyWriteFuture implements RpcWriteFuture {\n\tprivate final ChannelFuture future;\n\n\tpublic NettyWriteFuture(ChannelFuture future) {\n\t\tthis.future = Objects.requireNonNull(future);\n\t}\n\n\t@Override\n\tpublic boolean isWritten() {\n\t\treturn future.isSuccess();\n\t}\n\n\t@Override\n\tpublic Throwable getException() {\n\t\treturn future.cause();\n\t}\n\n\t@Override\n\tpublic void addListener(RpcWriteListener listener) {\n\t\tfuture.addListener(new NettyWriteListener(Objects.requireNonNull(listener), this));\n\t}\n\n\tprivate static final class NettyWriteListener implements GenericFutureListener<ChannelFuture> {\n\t\tprivate final RpcWriteListener listener;\n\t\tprivate final NettyWriteFuture writeFuture;\n\n\t\tpublic NettyWriteListener(RpcWriteListener listener, NettyWriteFuture writeFuture) {\n\t\t\tthis.listener = listener;\n\t\t\tthis.writeFuture = writeFuture;\n\t\t}\n\n\t\t@Override\n\t\tpublic void operationComplete(ChannelFuture future) throws Exception {\n\t\t\tlistener.afterWrited(writeFuture);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/RegistryConst.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.registry;\n\npublic interface RegistryConst {\n\tString SUMK_SOA_ROOT = \"/sumk_soa\";\n\n\tString METHODS = \"$.\";\n\tString METHOD_SPLIT = \"#\";\n\tString FEATURE = \"feature\";\n\tString WEIGHT = \"weight\";\n\tString START = \"start\";\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/RegistryFactory.java",
    "content": "package org.yx.rpc.registry;\n\nimport java.util.Optional;\n\nimport org.yx.base.Ordered;\nimport org.yx.rpc.registry.client.RegistryClient;\nimport org.yx.rpc.registry.server.RegistryServer;\n\n/**\n * 微服务注册中心。注册中心的地址由实现类决定，但是写入到注册中心的内容不受注册中心实现类的影响\n *\n */\npublic interface RegistryFactory extends Ordered {\n\n\tOptional<RegistryServer> registryServer();\n\n\tOptional<RegistryClient> registryClient();\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/client/RegistryClient.java",
    "content": "package org.yx.rpc.registry.client;\n\npublic interface RegistryClient {\n\n\tvoid watch() throws Exception;\n\n\tvoid stop();\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/client/RouteEvent.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.registry.client;\n\nimport org.yx.rpc.data.RouteInfo;\n\npublic class RouteEvent {\n\tprivate final RouteEventType type;\n\tprivate final String nodeName;\n\tprivate final RouteInfo route;\n\n\tpublic String getNodeName() {\n\t\treturn nodeName;\n\t}\n\n\tpublic RouteInfo getRoute() {\n\t\treturn route;\n\t}\n\n\tpublic RouteEventType getType() {\n\t\treturn type;\n\t}\n\n\tprivate RouteEvent(RouteEventType type, String nodeName, RouteInfo route) {\n\t\tthis.type = type;\n\t\tthis.nodeName = nodeName;\n\t\tthis.route = route;\n\t}\n\n\tpublic static RouteEvent createEvent(String nodeName, RouteInfo info) {\n\t\treturn new RouteEvent(RouteEventType.CREATE, nodeName, info);\n\t}\n\n\tpublic static RouteEvent deleteEvent(String nodeName) {\n\t\treturn new RouteEvent(RouteEventType.DELETE, nodeName, null);\n\t}\n\n\tpublic static RouteEvent modifyEvent(String nodeName, RouteInfo info) {\n\t\treturn new RouteEvent(RouteEventType.MODIFY, nodeName, info);\n\t}\n}"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/client/RouteEventType.java",
    "content": "package org.yx.rpc.registry.client;\n\npublic enum RouteEventType {\n\tCREATE, DELETE, MODIFY\n}"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/client/RouterHolder.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.registry.client;\n\nimport java.util.Collection;\nimport java.util.Objects;\nimport java.util.function.BiFunction;\n\nimport org.yx.common.Host;\nimport org.yx.common.route.Router;\nimport org.yx.common.route.Routes;\nimport org.yx.common.route.WeightedServer;\n\npublic final class RouterHolder {\n\n\tprivate static BiFunction<String, Collection<WeightedServer<Host>>, Router<Host>> routerFactory = (api,\n\t\t\tservers) -> Routes.createWeightedRouter(servers);\n\n\tpublic static void set(BiFunction<String, Collection<WeightedServer<Host>>, Router<Host>> factory) {\n\t\tRouterHolder.routerFactory = Objects.requireNonNull(factory);\n\t}\n\n\tpublic static BiFunction<String, Collection<WeightedServer<Host>>, Router<Host>> get() {\n\t\treturn RouterHolder.routerFactory;\n\t}\n\n\tpublic static Router<Host> createRouter(String api, Collection<WeightedServer<Host>> servers) {\n\t\treturn routerFactory.apply(api, servers);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/client/RpcRoutes.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.registry.client;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.yx.common.Host;\nimport org.yx.common.route.Router;\nimport org.yx.common.route.WeightedServer;\nimport org.yx.log.Log;\nimport org.yx.rpc.data.ApiProfile;\nimport org.yx.rpc.data.RouteInfo;\nimport org.yx.util.CollectionUtil;\n\npublic final class RpcRoutes {\n\n\tprivate final Map<String, Router<Host>> rpcRoutes;\n\n\tprivate final List<RouteInfo> zkDatas;\n\tprivate final Map<Host, Integer> protocols;\n\tprivate final long createTime;\n\n\tprivate RpcRoutes(Collection<RouteInfo> zkDatas, Map<String, Router<Host>> routes) {\n\t\tthis.zkDatas = CollectionUtil.unmodifyList(Objects.requireNonNull(zkDatas));\n\t\tthis.rpcRoutes = Objects.requireNonNull(routes);\n\t\tMap<Host, Integer> p = new HashMap<>();\n\t\tfor (RouteInfo info : zkDatas) {\n\t\t\tp.put(info.host(), info.feature());\n\t\t}\n\t\tthis.protocols = p;\n\t\tthis.createTime = System.currentTimeMillis();\n\t}\n\n\tprivate static volatile RpcRoutes ROUTE = new RpcRoutes(Collections.emptyList(), Collections.emptyMap());\n\n\tpublic static Set<Host> servers() {\n\t\treturn new HashSet<>(ROUTE.protocols.keySet());\n\t}\n\n\tpublic static int getServerProtocol(Host url) {\n\t\tInteger p = ROUTE.protocols.get(url);\n\t\tif (p == null) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn p.intValue();\n\t}\n\n\tpublic static Router<Host> getRoute(String api) {\n\t\treturn ROUTE.rpcRoutes.get(api);\n\t}\n\n\tpublic static int routeSize() {\n\t\treturn ROUTE.rpcRoutes.size();\n\t}\n\n\tprivate static void _refresh(Collection<RouteInfo> rawData, Map<String, Router<Host>> route) {\n\t\tRpcRoutes r = new RpcRoutes(rawData, route);\n\t\tRpcRoutes.ROUTE = r;\n\t\tif (Log.get(\"sumk.rpc.client\").isTraceEnabled()) {\n\t\t\tStringBuilder sb = new StringBuilder(\"微服务源:\");\n\t\t\tfor (RouteInfo d : rawData) {\n\t\t\t\tsb.append(\"  \").append(d.host());\n\t\t\t}\n\t\t\tLog.get(\"sumk.rpc.client\").trace(sb.toString());\n\t\t}\n\t}\n\n\tprivate static void fillWeightedServer(Map<String, WeightedServer<Host>> source,\n\t\t\tMap<String, Set<WeightedServer<Host>>> dest) {\n\t\tfor (Entry<String, WeightedServer<Host>> entry : source.entrySet()) {\n\t\t\tString m = entry.getKey();\n\t\t\tWeightedServer<Host> serverMachine = entry.getValue();\n\t\t\tSet<WeightedServer<Host>> server = dest.get(m);\n\t\t\tif (server == null) {\n\t\t\t\tserver = new HashSet<>();\n\t\t\t\tdest.put(m, server);\n\t\t\t}\n\t\t\tserver.add(serverMachine);\n\t\t}\n\t}\n\n\tpublic static synchronized void refresh(Collection<RouteInfo> datas) {\n\t\tMap<String, Set<WeightedServer<Host>>> map = new HashMap<>();\n\t\tfor (RouteInfo r : datas) {\n\t\t\tfillWeightedServer(createServerMachine(r), map);\n\t\t}\n\t\tMap<String, Router<Host>> routes = new HashMap<>();\n\t\tfor (Entry<String, Set<WeightedServer<Host>>> entry : map.entrySet()) {\n\t\t\tString method = entry.getKey();\n\t\t\tSet<WeightedServer<Host>> servers = entry.getValue();\n\t\t\tif (servers == null || servers.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tRouter<Host> route = RouterHolder.createRouter(method, servers);\n\t\t\tif (route != null) {\n\t\t\t\troutes.put(method, route);\n\t\t\t}\n\t\t}\n\t\t_refresh(datas, routes);\n\t}\n\n\tprivate static Map<String, WeightedServer<Host>> createServerMachine(RouteInfo data) {\n\t\tMap<String, WeightedServer<Host>> servers = new HashMap<>();\n\t\tint weight = data.weight() > 0 ? data.weight() : 100;\n\t\tfor (ApiProfile intf : data.apis()) {\n\t\t\tWeightedServer<Host> server = new WeightedHost(data.host(), weight);\n\t\t\tservers.put(intf.getName(), server);\n\t\t}\n\t\treturn servers;\n\t}\n\n\tpublic static RpcRoutes current() {\n\t\treturn ROUTE;\n\t}\n\n\tpublic Map<String, Router<Host>> routes() {\n\t\treturn CollectionUtil.unmodifyMap(this.rpcRoutes);\n\t}\n\n\tpublic List<RouteInfo> zkDatas() {\n\t\treturn this.zkDatas;\n\t}\n\n\tpublic long createTime() {\n\t\treturn this.createTime;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/client/WeightedHost.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.registry.client;\n\nimport org.yx.common.Host;\nimport org.yx.common.route.AbstractWeightedServer;\nimport org.yx.rpc.client.HostChecker;\n\npublic class WeightedHost extends AbstractWeightedServer<Host> {\n\n\tpublic WeightedHost(Host source, int weight) {\n\t\tsuper(source);\n\t\tthis.setWeight(weight);\n\t}\n\n\t@Override\n\tpublic boolean isEnable() {\n\t\treturn !HostChecker.get().isDowned(this.source);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/server/RegistryHelper.java",
    "content": "package org.yx.rpc.registry.server;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.common.Host;\nimport org.yx.conf.AppInfo;\nimport org.yx.rpc.Profile;\nimport org.yx.rpc.context.RpcActions;\nimport org.yx.rpc.data.RouteDataOperators;\nimport org.yx.rpc.registry.RegistryConst;\n\npublic class RegistryHelper {\n\n\tpublic static String soaRoot() {\n\t\treturn AppInfo.get(\"sumk.rpc.zk.root.server\", \"sumk.rpc.zk.root\", RegistryConst.SUMK_SOA_ROOT);\n\t}\n\n\tpublic static String fullPath(final Host host) {\n\t\tStringBuilder sb = new StringBuilder().append(soaRoot()).append('/')\n\t\t\t\t.append(RouteDataOperators.inst().getName(host));\n\t\treturn sb.toString();\n\t}\n\n\tpublic static byte[] routeData(final Host host) throws Exception {\n\t\tList<String> apis = RpcActions.publishSoaSet();\n\t\tfinal Map<String, String> map = new HashMap<>();\n\t\tfor (String api : apis) {\n\n\t\t\tmap.put(RegistryConst.METHODS + api, AppInfo.get(\"sumk.rpc.api.\" + api));\n\t\t}\n\t\tmap.put(RegistryConst.FEATURE, Profile.featureInHex());\n\t\tmap.put(RegistryConst.START, String.valueOf(System.currentTimeMillis()));\n\t\tmap.put(RegistryConst.WEIGHT, AppInfo.get(\"sumk.rpc.weight\", \"100\"));\n\n\t\treturn RouteDataOperators.inst().serialize(host, map);\n\t}\n\n\tpublic static boolean needRegisterServer() {\n\t\treturn AppInfo.getBoolean(\"sumk.rpc.server.register\", true);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/server/RegistryServer.java",
    "content": "package org.yx.rpc.registry.server;\n\nimport org.yx.base.Ordered;\nimport org.yx.common.Host;\n\n/**\n * 微服务注册中心\n *\n */\npublic interface RegistryServer extends Ordered {\n\n\t/**\n\t * 开启微服务对注册中心的注册及变更监听\n\t * \n\t * @param serverHost 注册中心里的本机地址\n\t */\n\tvoid start(Host serverHost);\n\n\tvoid stop();\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/SimpleZkSerializer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.registry.zookeeper;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\n\nimport org.I0Itec.zkclient.serialize.ZkSerializer;\nimport org.yx.common.util.S;\n\npublic class SimpleZkSerializer implements ZkSerializer {\n\n\tprivate final Charset charset;\n\n\tpublic SimpleZkSerializer() {\n\t\tthis(StandardCharsets.UTF_8);\n\t}\n\n\tpublic SimpleZkSerializer(Charset charset) {\n\t\tthis.charset = charset;\n\t}\n\n\t@Override\n\tpublic byte[] serialize(Object data) {\n\t\tif (data == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (byte[].class == data.getClass()) {\n\t\t\treturn (byte[]) data;\n\t\t}\n\t\tif (String.class == data.getClass()) {\n\t\t\treturn ((String) data).getBytes(charset);\n\t\t}\n\t\treturn S.json().toJson(data).getBytes(charset);\n\t}\n\n\t@Override\n\tpublic Object deserialize(byte[] bytes) {\n\t\treturn bytes;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/ZKSystemConfig.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.registry.zookeeper;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.I0Itec.zkclient.IZkDataListener;\nimport org.I0Itec.zkclient.ZkClient;\nimport org.yx.conf.MultiNodeConfig;\nimport org.yx.exception.SumkException;\nimport org.yx.log.RawLog;\nimport org.yx.util.CollectionUtil;\n\n/**\n * 用于操作zk中的appinfo对应的变量信息， node在前的优先级高\n */\npublic class ZKSystemConfig extends MultiNodeConfig {\n\n\tprivate final List<String> dataPaths;\n\tprivate final String zkUrl;\n\n\tpublic ZKSystemConfig(String zkUrl, String root, List<String> nodes) {\n\t\tthis.zkUrl = Objects.requireNonNull(zkUrl);\n\t\tif (root == null) {\n\t\t\troot = \"/\";\n\t\t}\n\t\tif (!root.endsWith(\"/\")) {\n\t\t\troot = root + \"/\";\n\t\t}\n\t\tList<String> ps = new ArrayList<>(Objects.requireNonNull(nodes).size());\n\t\tfor (String node : nodes) {\n\t\t\tnode = node.trim();\n\t\t\tif (node.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (node.startsWith(\"/\")) {\n\t\t\t\tnode = node.substring(1);\n\t\t\t}\n\t\t\tps.add(root + node);\n\t\t}\n\t\tif (ps.isEmpty()) {\n\t\t\tthrow new SumkException(4325213, \"zk路径不能为空\");\n\t\t}\n\t\tthis.dataPaths = CollectionUtil.unmodifyList(ps);\n\t}\n\n\tpublic List<String> getDataPaths() {\n\t\treturn dataPaths;\n\t}\n\n\tpublic String getZkUrl() {\n\t\treturn zkUrl;\n\t}\n\n\tprotected IZkDataListener listener = new IZkDataListener() {\n\n\t\t@Override\n\t\tpublic void handleDataChange(String dataPath, Object data) throws Exception {\n\t\t\tonChange(dataPath);\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleDataDeleted(String dataPath) throws Exception {\n\t\t\tonChange(dataPath);\n\t\t}\n\t};\n\n\tprotected void onChange(String dataPath) {\n\t\ttry {\n\t\t\tif (dataPath != null) {\n\t\t\t\tRawLog.info(LOG_NAME, \"data in zk path \" + dataPath + \" changed\");\n\t\t\t}\n\t\t\tZkClient client = ZkClientHelper.getZkClient(zkUrl);\n\t\t\tMap<String, String> map = new HashMap<>();\n\t\t\tList<String> list = new ArrayList<>(dataPaths);\n\t\t\tCollections.reverse(list);\n\t\t\tfor (String path : list) {\n\t\t\t\tif (!client.exists(path)) {\n\t\t\t\t\tRawLog.error(LOG_NAME, \"该zk地址不存在: \" + path);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbyte[] data = client.readData(path);\n\t\t\t\tif (data == null || data.length == 0) {\n\t\t\t\t\tRawLog.debug(LOG_NAME, path + \" is empty\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tMap<String, String> tmp = parse(data);\n\t\t\t\tmap.putAll(tmp);\n\t\t\t}\n\t\t\tthis.config = map;\n\t\t\tonRefresh();\n\t\t} catch (Exception e) {\n\t\t\tRawLog.error(LOG_NAME, e);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void init() {\n\t\tString zkUrl = getZkUrl();\n\t\tthis.onChange(null);\n\t\tZkClient client = ZkClientHelper.getZkClient(zkUrl);\n\t\tfor (String path : this.dataPaths) {\n\t\t\tclient.subscribeDataChanges(path, listener);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\ttry {\n\t\t\tZkClient client = ZkClientHelper.getZkClient(zkUrl);\n\t\t\tfor (String path : this.dataPaths) {\n\t\t\t\tclient.unsubscribeDataChanges(path, listener);\n\t\t\t}\n\t\t\tthis.started = false;\n\t\t} catch (Exception e) {\n\t\t\tRawLog.error(LOG_NAME, e.getMessage(), e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/ZkClientHelper.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.registry.zookeeper;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.I0Itec.zkclient.ZkClient;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\n\npublic final class ZkClientHelper {\n\tprivate static Map<String, ZkClient> CLIENTS = Collections.emptyMap();\n\n\tpublic static void makeSure(ZkClient client, final String dataPath) {\n\t\tint start = 0, index;\n\t\twhile (true) {\n\t\t\tindex = dataPath.indexOf(\"/\", start + 1);\n\n\t\t\tif (index == start + 1) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString path = dataPath;\n\t\t\tif (index > 0) {\n\t\t\t\tpath = dataPath.substring(0, index);\n\t\t\t\tstart = index;\n\t\t\t}\n\t\t\tif (!client.exists(path)) {\n\t\t\t\ttry {\n\t\t\t\t\tclient.createPersistent(path);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tif (!(e instanceof ZkNodeExistsException)) {\n\t\t\t\t\t\tLogs.system().warn(path + \" create failed.\", e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (index < 0 || index == dataPath.length() - 1) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static ZkClient getZkClient(String url) {\n\t\tZkClient zk = CLIENTS.get(url);\n\t\tif (zk != null) {\n\t\t\treturn zk;\n\t\t}\n\t\tsynchronized (ZkClientHelper.class) {\n\t\t\tMap<String, ZkClient> map = new HashMap<>(CLIENTS);\n\t\t\tzk = map.get(url);\n\t\t\tif (zk != null) {\n\t\t\t\treturn zk;\n\t\t\t}\n\t\t\tzk = new ZkClient(url, AppInfo.getInt(\"sumk.zk.session.timeout\", 8000),\n\t\t\t\t\tAppInfo.getInt(\"sumk.zk.connection.timeout\", 20000));\n\t\t\tzk.setZkSerializer(new SimpleZkSerializer());\n\t\t\tmap.put(url, zk);\n\t\t\tCLIENTS = map;\n\t\t}\n\t\treturn CLIENTS.get(url);\n\t}\n\n\tpublic static synchronized ZkClient remove(String url) {\n\t\tMap<String, ZkClient> map = new HashMap<>(CLIENTS);\n\t\tZkClient zk = map.remove(url);\n\t\tif (zk == null) {\n\t\t\treturn null;\n\t\t}\n\t\tCLIENTS = map;\n\t\treturn zk;\n\t}\n\n\tpublic static Set<String> cachedUrls() {\n\t\treturn new HashSet<>(CLIENTS.keySet());\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/ZkRegistryClient.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.registry.zookeeper;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Predicate;\n\nimport org.I0Itec.zkclient.IZkChildListener;\nimport org.I0Itec.zkclient.IZkDataListener;\nimport org.I0Itec.zkclient.ZkClient;\nimport org.slf4j.Logger;\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.base.matcher.Matchers;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Log;\nimport org.yx.rpc.data.RouteDataOperators;\nimport org.yx.rpc.data.RouteInfo;\nimport org.yx.rpc.data.RoutePathData;\nimport org.yx.rpc.registry.RegistryConst;\nimport org.yx.rpc.registry.client.RegistryClient;\nimport org.yx.rpc.registry.client.RouteEvent;\nimport org.yx.rpc.registry.client.RpcRoutes;\nimport org.yx.util.StringUtil;\nimport org.yx.util.SumkThreadPool;\n\npublic class ZkRegistryClient implements RegistryClient {\n\tprivate final String zkUrl;\n\tprivate Set<String> childs = Collections.emptySet();\n\tprivate final Predicate<String> matcher;\n\tprivate final String SOA_ROOT = AppInfo.get(\"sumk.rpc.zk.root.client\", \"sumk.rpc.zk.root\",\n\t\t\tRegistryConst.SUMK_SOA_ROOT);\n\tprivate Logger logger = Log.get(\"sumk.rpc.client\");\n\tprivate final ThreadPoolExecutor executor;\n\tprivate final BlockingQueue<RouteEvent> queue = new LinkedBlockingQueue<>();\n\n\tpublic ZkRegistryClient(String zkUrl) {\n\t\tthis.zkUrl = zkUrl;\n\n\t\tthis.matcher = Matchers.includeAndExclude(AppInfo.getLatin(\"sumk.rpc.server.include\", \"*\"),\n\t\t\t\tAppInfo.getLatin(\"sumk.rpc.server.exclude\", null));\n\n\t\texecutor = new ThreadPoolExecutor(1, 1, 5000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1000),\n\t\t\t\tSumkThreadPool.createThreadFactory(\"rpc-client-\"), new ThreadPoolExecutor.DiscardPolicy());\n\t\texecutor.allowCoreThreadTimeOut(true);\n\t}\n\n\t@Override\n\tpublic void watch() throws IOException {\n\t\tif (StringUtil.isEmpty(this.zkUrl)) {\n\t\t\treturn;\n\t\t}\n\t\tList<RouteInfo> datas = new ArrayList<>();\n\t\tZkClient zk = ZkClientHelper.getZkClient(zkUrl);\n\t\tZkClientHelper.makeSure(zk, SOA_ROOT);\n\n\t\tfinal IZkDataListener nodeListener = new IZkDataListener() {\n\t\t\tZkRegistryClient parser = ZkRegistryClient.this;\n\n\t\t\t@Override\n\t\t\tpublic void handleDataChange(String dataPath, Object data) throws Exception {\n\t\t\t\tlogger.trace(\"{} node changed\", dataPath);\n\t\t\t\tint index = dataPath.lastIndexOf(\"/\");\n\t\t\t\tif (index > 0) {\n\t\t\t\t\tdataPath = dataPath.substring(index + 1);\n\t\t\t\t}\n\t\t\t\tRouteInfo d = RouteDataOperators.inst().deserialize(new RoutePathData(dataPath, (byte[]) data));\n\t\t\t\tif (d == null || d.apis().isEmpty()) {\n\t\t\t\t\tlogger.debug(\"{} has no interface or is invalid node\", dataPath);\n\t\t\t\t\tparser.handle(RouteEvent.deleteEvent(dataPath));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tparser.handle(RouteEvent.modifyEvent(dataPath, d));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void handleDataDeleted(String dataPath) throws Exception {\n\n\t\t\t}\n\n\t\t};\n\n\t\tList<String> paths = zk.subscribeChildChanges(SOA_ROOT, new IZkChildListener() {\n\t\t\tZkRegistryClient parser = ZkRegistryClient.this;\n\n\t\t\t@Override\n\t\t\tpublic void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {\n\t\t\t\tif (currentChilds == null) {\n\t\t\t\t\tcurrentChilds = Collections.emptyList();\n\t\t\t\t}\n\t\t\t\tList<String> ips = filter(currentChilds);\n\n\t\t\t\tList<String> createChilds = new ArrayList<>();\n\t\t\t\tSet<String> deleteChilds = new HashSet<>(parser.childs);\n\t\t\t\tfor (String zkChild : ips) {\n\t\t\t\t\tboolean exist = deleteChilds.remove(zkChild);\n\n\t\t\t\t\tif (!exist) {\n\t\t\t\t\t\tcreateChilds.add(zkChild);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tZkClient zkClient = ZkClientHelper.getZkClient(zkUrl);\n\t\t\t\tparser.childs = new HashSet<>(ips);\n\t\t\t\tfor (String create : createChilds) {\n\t\t\t\t\tlogger.trace(\"{} node created\", create);\n\t\t\t\t\tRouteInfo d = parser.getZkNodeData(zkClient, create);\n\t\t\t\t\tif (d == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tparser.handle(RouteEvent.createEvent(create, d));\n\t\t\t\t\tzk.subscribeDataChanges(parentPath + \"/\" + create, nodeListener);\n\t\t\t\t}\n\n\t\t\t\tfor (String delete : deleteChilds) {\n\t\t\t\t\tlogger.trace(\"{} node deleted\", delete);\n\t\t\t\t\tparser.handle(RouteEvent.deleteEvent(delete));\n\t\t\t\t\tzk.unsubscribeDataChanges(parentPath + \"/\" + delete, nodeListener);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tif (paths == null) {\n\t\t\tpaths = Collections.emptyList();\n\t\t}\n\t\tpaths = filter(paths);\n\t\tif (logger.isDebugEnabled()) {\n\t\t\tlogger.debug(\"valid rpc servers: {}\", paths);\n\t\t}\n\t\tthis.childs = new HashSet<>(paths);\n\t\tfor (String path : paths) {\n\t\t\tRouteInfo d = getZkNodeData(zk, path);\n\t\t\tif (d == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tzk.subscribeDataChanges(SOA_ROOT + \"/\" + path, nodeListener);\n\t\t\tdatas.add(d);\n\t\t}\n\t\tRpcRoutes.refresh(datas);\n\t}\n\n\tprivate List<String> filter(List<String> currentChilds) {\n\t\tif (this.matcher == BooleanMatcher.TRUE) {\n\t\t\treturn currentChilds;\n\t\t}\n\t\tList<String> ips = new ArrayList<>(currentChilds.size());\n\t\tfor (String ip : currentChilds) {\n\t\t\tif (this.matcher.test(ip)) {\n\t\t\t\tips.add(ip);\n\t\t\t}\n\t\t}\n\t\treturn ips;\n\t}\n\n\tprivate RouteInfo getZkNodeData(ZkClient zk, String path) {\n\t\tbyte[] data = zk.readData(SOA_ROOT + \"/\" + path);\n\t\ttry {\n\t\t\treturn RouteDataOperators.inst().deserialize(new RoutePathData(path, data));\n\t\t} catch (Exception e) {\n\t\t\tlogger.error(\"解析\" + path + \"的zk数据失败\", e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic void handle(RouteEvent event) {\n\t\tif (event == null) {\n\t\t\treturn;\n\t\t}\n\t\tqueue.offer(event);\n\t\tthis.executor.execute(() -> {\n\t\t\tif (queue.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsynchronized (ZkRegistryClient.this) {\n\t\t\t\tList<RouteEvent> list = new ArrayList<>();\n\t\t\t\tqueue.drainTo(list);\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tMap<String, RouteInfo> map = new HashMap<>();\n\t\t\t\tfor (RouteInfo info : RpcRoutes.current().zkDatas()) {\n\t\t\t\t\tmap.put(info.path(), info);\n\t\t\t\t}\n\t\t\t\tif (handleData(map, list) > 0) {\n\t\t\t\t\tRpcRoutes.refresh(map.values());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate int handleData(Map<String, RouteInfo> data, List<RouteEvent> list) {\n\t\tint count = 0;\n\t\tfor (RouteEvent event : list) {\n\t\t\tif (event == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tswitch (event.getType()) {\n\t\t\tcase CREATE:\n\t\t\tcase MODIFY:\n\t\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"{}: {} {}\", count, event.getType(), event.getNodeName());\n\t\t\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\t\t\tlogger.trace(\"event的接口列表：{}\", event.getRoute().apis());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdata.put(event.getNodeName(), event.getRoute());\n\t\t\t\tcount++;\n\t\t\t\tbreak;\n\t\t\tcase DELETE:\n\n\t\t\t\tif (data.remove(event.getNodeName()) != null) {\n\t\t\t\t\tlogger.debug(\"{}: {} {}\", count, event.getType(), event.getNodeName());\n\t\t\t\t\tcount++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlogger.info(\"{}: {}已经被移除了\", count, event.getNodeName());\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn count;\n\t}\n\n\t@Override\n\tpublic void stop() {\n\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/ZkRegistryFactory.java",
    "content": "package org.yx.rpc.registry.zookeeper;\n\nimport java.util.Optional;\n\nimport org.yx.annotation.Bean;\nimport org.yx.base.Ordered;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\nimport org.yx.rpc.registry.RegistryFactory;\nimport org.yx.rpc.registry.client.RegistryClient;\nimport org.yx.rpc.registry.server.RegistryServer;\nimport org.yx.util.StringUtil;\n\n@Bean\npublic class ZkRegistryFactory implements RegistryFactory {\n\n\tprivate static final String ZK_URL = \"sumk.zkurl\";\n\n\t@Override\n\tpublic int order() {\n\t\treturn Ordered.DEFAULT_ORDER + 1000;\n\t}\n\n\t@Override\n\tpublic Optional<RegistryServer> registryServer() {\n\t\tString zkUrl = zkServerUrl();\n\t\tif (StringUtil.isEmpty(zkUrl)) {\n\t\t\tLogs.rpc().warn(\"##因为没有配置{},所以无法注册接口到注册中心##\", ZK_URL);\n\t\t\treturn Optional.empty();\n\t\t}\n\t\treturn Optional.of(new ZkRegistryServer(zkUrl));\n\t}\n\n\t@Override\n\tpublic Optional<RegistryClient> registryClient() {\n\t\tString zkUrl = zkClinetUrl();\n\t\tif (StringUtil.isEmpty(zkUrl)) {\n\t\t\tLogs.rpc().warn(\"##因为没有配置{},所以只能调用本机的微服务接口##\", ZK_URL);\n\t\t\treturn Optional.empty();\n\t\t}\n\t\treturn Optional.of(new ZkRegistryClient(zkUrl));\n\t}\n\n\tpublic static String zkServerUrl() {\n\t\tString url = AppInfo.get(\"sumk.rpc.zk.server\");\n\t\tif (url != null && url.length() > 0) {\n\t\t\treturn url;\n\t\t}\n\t\treturn AppInfo.get(ZK_URL);\n\t}\n\n\tpublic static String zkClinetUrl() {\n\t\tString url = AppInfo.get(\"sumk.rpc.zk.client\");\n\t\tif (url != null && url.length() > 0) {\n\t\t\treturn url;\n\t\t}\n\t\treturn AppInfo.get(ZK_URL);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/ZkRegistryServer.java",
    "content": "package org.yx.rpc.registry.zookeeper;\n\nimport java.util.Objects;\n\nimport org.I0Itec.zkclient.IZkStateListener;\nimport org.I0Itec.zkclient.ZkClient;\nimport org.apache.zookeeper.Watcher.Event.KeeperState;\nimport org.slf4j.Logger;\nimport org.yx.base.Ordered;\nimport org.yx.common.Host;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.rpc.registry.server.RegistryHelper;\nimport org.yx.rpc.registry.server.RegistryServer;\nimport org.yx.util.StringUtil;\n\npublic class ZkRegistryServer implements RegistryServer {\n\n\tprivate final Logger logger = Log.get(\"sumk.rpc.registry.zookeeper\");\n\n\tprivate volatile boolean started;\n\n\tprivate Host host;\n\n\tprivate boolean needRegister;\n\n\tprivate final String zkUrl;\n\n\tpublic ZkRegistryServer(String zkUrl) {\n\t\tthis.zkUrl = zkUrl;\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn Ordered.DEFAULT_ORDER + 1000;\n\t}\n\n\tprivate static boolean soaServerEnable() {\n\t\treturn AppInfo.getBoolean(\"sumk.rpc.server.register\", true);\n\t}\n\n\t@Override\n\tpublic synchronized void start(Host serverHost) {\n\t\tif (started || host != null || StringUtil.isEmpty(zkUrl)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.host = Objects.requireNonNull(serverHost);\n\t\ttry {\n\t\t\tZkClient client = zkClient();\n\t\t\tZkClientHelper.makeSure(client, RegistryHelper.soaRoot());\n\t\t\tthis.needRegister = soaServerEnable();\n\t\t\tif (needRegister) {\n\t\t\t\tthis.zkRegister.run();\n\t\t\t\tlogger.info(\"registe zk at : {}\", this.host);\n\t\t\t} else {\n\t\t\t\tthis.zkUnRegister.run();\n\t\t\t}\n\t\t\tAppInfo.addObserver(info -> {\n\t\t\t\tif (!ZkRegistryServer.this.started) {\n\t\t\t\t\tlogger.debug(\"soa server has not started\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tboolean serverEnable = soaServerEnable();\n\t\t\t\tif (serverEnable == needRegister) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tif (serverEnable) {\n\t\t\t\t\t\tZkRegistryServer.this.zkRegister.run();\n\t\t\t\t\t\tlogger.info(\"soa server [{}] enabled\", host);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tZkRegistryServer.this.zkUnRegister.run();\n\t\t\t\t\t\tlogger.info(\"soa server [{}] disabled!!!\", host);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlogger.error(e.getLocalizedMessage(), e);\n\t\t\t\t}\n\t\t\t});\n\t\t\tstarted = true;\n\t\t} catch (Exception e) {\n\t\t\tlogger.error(e.getLocalizedMessage(), e);\n\t\t\tthrow new SumkException(35334546, \"soa服务启动失败\");\n\t\t}\n\n\t}\n\n\tprivate final IZkStateListener stateListener = new IZkStateListener() {\n\t\t@Override\n\t\tpublic void handleStateChanged(KeeperState state) throws Exception {\n\t\t\tlogger.debug(\"zk state changed:{}\", state);\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleNewSession() throws Exception {\n\t\t\tbyte[] data = createZkPathData();\n\t\t\tZkClientHelper.getZkClient(zkUrl).createEphemeral(fullPath(), data);\n\t\t\tlogger.debug(\"handleNewSession\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleSessionEstablishmentError(Throwable error) throws Exception {\n\t\t\tlogger.error(\"SessionEstablishmentError#\" + error.getMessage(), error);\n\t\t}\n\n\t};\n\n\tprivate ZkClient zkClient() {\n\t\treturn ZkClientHelper.getZkClient(zkUrl);\n\t}\n\n\tprivate final Runnable zkUnRegister = () -> {\n\t\tZkClient client = zkClient();\n\t\tclient.unsubscribeStateChanges(stateListener);\n\t\tclient.delete(fullPath());\n\t};\n\n\tprivate final Runnable zkRegister = () -> {\n\t\tZkClient client = zkClient();\n\t\tbyte[] data = null;\n\t\ttry {\n\t\t\tdata = createZkPathData();\n\t\t} catch (Exception e) {\n\t\t\tlogger.error(e.getLocalizedMessage(), e);\n\t\t\treturn;\n\t\t}\n\n\t\tzkUnRegister.run();\n\t\tclient.createEphemeral(fullPath(), data);\n\t\tclient.subscribeStateChanges(stateListener);\n\t};\n\n\t@Override\n\tpublic synchronized void stop() {\n\t\tif (StringUtil.isEmpty(zkUrl)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tZkClient client = ZkClientHelper.remove(zkUrl);\n\t\t\tif (client != null) {\n\t\t\t\tclient.unsubscribeAll();\n\t\t\t\tclient.delete(fullPath());\n\t\t\t\tclient.close();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlogger.error(e.getLocalizedMessage(), e);\n\t\t}\n\n\t\tstarted = false;\n\t}\n\n\tprivate String fullPath() {\n\t\treturn RegistryHelper.fullPath(host);\n\t}\n\n\tprivate byte[] createZkPathData() throws Exception {\n\t\treturn RegistryHelper.routeData(host);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/LocalRequestHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.yx.bean.IOC;\nimport org.yx.exception.SoaException;\nimport org.yx.rpc.RpcErrorCode;\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.context.RpcActionNode;\n\npublic class LocalRequestHandler {\n\tpublic static final LocalRequestHandler inst = new LocalRequestHandler();\n\tprivate List<RequestHandler> handlers;\n\n\tpublic List<RequestHandler> getHandlers() {\n\t\treturn handlers;\n\t}\n\n\tpublic void setHandlers(List<RequestHandler> handlers) {\n\t\tthis.handlers = Objects.requireNonNull(handlers);\n\t}\n\n\tprivate LocalRequestHandler() {\n\t\thandlers = IOC.getBeans(RequestHandler.class);\n\t}\n\n\tpublic Response handler(Request request, RpcActionNode action) {\n\t\tResponse resp = new Response();\n\t\ttry {\n\t\t\tfor (RequestHandler h : this.handlers) {\n\t\t\t\tif (h.handle(request, resp)) {\n\t\t\t\t\tresp.serviceInvokeMilTime(System.currentTimeMillis() - request.getStartInServer());\n\t\t\t\t\treturn resp;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Throwable e) {\n\t\t\tresp.exception(SoaException.create(RpcErrorCode.SERVER_UNKNOW, \"server handler error\", e));\n\t\t}\n\t\tlong begin = request.getStartInServer();\n\t\tresp.serviceInvokeMilTime(System.currentTimeMillis() - begin);\n\t\tif (resp.exception() == null) {\n\t\t\tresp.exception(new SoaException(RpcErrorCode.NO_MAPPED_UNKNOW, \"没有合适的handler\", \"no accepted handler\"));\n\t\t}\n\t\treturn resp;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/LocalRpcContext.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server;\n\npublic final class LocalRpcContext {\n\n\tprivate static final ThreadLocal<RpcContext> CTX = new ThreadLocal<>();\n\n\tpublic static RpcContext getCtx() {\n\t\treturn CTX.get();\n\t}\n\n\tpublic static void setCtx(RpcContext ctx) {\n\t\tCTX.set(ctx);\n\t}\n\n\tpublic static void remove() {\n\t\tCTX.remove();\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/RequestHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server;\n\nimport org.yx.base.Ordered;\nimport org.yx.rpc.codec.Request;\n\npublic interface RequestHandler extends Ordered {\n\n\tboolean handle(Request request, Response resp);\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/Response.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server;\n\nimport java.io.Serializable;\n\nimport org.yx.common.util.S;\nimport org.yx.exception.SoaException;\nimport org.yx.exception.SumkException;\nimport org.yx.rpc.codec.CodecKit;\nimport org.yx.rpc.codec.DataStream;\nimport org.yx.rpc.codec.Protocols;\nimport org.yx.rpc.codec.StreamAble;\n\npublic class Response implements StreamAble, Serializable {\n\n\tprivate static final long serialVersionUID = 1L;\n\tprivate static final byte BODY_TYPE_JSON = 1;\n\tprivate static final byte BODY_TYPE_EXCEPTION = 2;\n\n\tprivate String sn;\n\tprivate String json;\n\tprivate SoaException exception;\n\n\tprivate long _ms = -1;\n\n\tprivate int _clientProtocol;\n\n\tpublic String sn() {\n\t\treturn sn;\n\t}\n\n\tpublic String json() {\n\t\treturn json;\n\t}\n\n\tpublic long serviceInvokeMilTime() {\n\t\treturn _ms;\n\t}\n\n\tpublic void sn(String sn) {\n\t\tthis.sn = sn;\n\t}\n\n\tpublic void json(String json) {\n\t\tthis.json = json;\n\t}\n\n\tpublic void serviceInvokeMilTime(long ms) {\n\t\tthis._ms = ms;\n\t}\n\n\tpublic Response() {\n\t}\n\n\tpublic Response(String sn) {\n\t\tthis.sn = sn;\n\t}\n\n\tpublic SoaException exception() {\n\t\treturn exception;\n\t}\n\n\tpublic void exception(SoaException exception) {\n\t\tthis.exception = exception;\n\t}\n\n\tpublic boolean isSuccess() {\n\t\treturn this.exception == null;\n\t}\n\n\tpublic int getClientProtocol() {\n\t\treturn _clientProtocol;\n\t}\n\n\tpublic void setClientProtocol(int clientAcceptedProtocol) {\n\t\tthis._clientProtocol = clientAcceptedProtocol;\n\t}\n\n\tpublic void writeTo(DataStream s) throws Exception {\n\t\ts.writeInt(1, 1);\n\t\ts.writePrefixedString(this.sn, 1);\n\n\t\ts.writeInt(0, 1);\n\t\tif (this.exception != null) {\n\t\t\ts.writeInt(BODY_TYPE_EXCEPTION, 1);\n\t\t\tString strException = S.json().toJson(exception);\n\t\t\ts.writePrefixedString(strException, 4);\n\t\t\treturn;\n\t\t}\n\n\t\ts.writeInt(BODY_TYPE_JSON, 1);\n\t\ts.writePrefixedString(json, 4);\n\n\t}\n\n\tpublic void readFrom(DataStream s) throws Exception {\n\n\t\tint size = s.readInt(1);\n\t\tthis.sn = s.readPrefixedString(1);\n\t\tCodecKit.skipPrefixedString(s, size - 1);\n\n\t\tsize = s.readInt(1);\n\t\tif (size > 0) {\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\ts.readPrefixedString(1);\n\t\t\t\ts.readPrefixedString(2);\n\t\t\t}\n\t\t}\n\n\t\tint type = s.readInt(1);\n\t\tswitch (type) {\n\t\tcase BODY_TYPE_JSON:\n\t\t\tthis.json = s.readPrefixedString(4);\n\t\t\tbreak;\n\t\tcase BODY_TYPE_EXCEPTION:\n\t\t\tString ex = s.readPrefixedString(4);\n\t\t\tthis.exception = S.json().fromJson(ex, SoaException.class);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow new SumkException(45432239, \"不支持resp的body类型：\" + type);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getMessageType() {\n\t\treturn Protocols.RESPONSE;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/RpcContext.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server;\n\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.bean.aop.context.NodeContext;\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.context.RpcActionNode;\n\npublic class RpcContext extends NodeContext<RpcActionNode> {\n\t@NotNull\n\tprivate final RpcActionNode node;\n\t@NotNull\n\tprotected final Request req;\n\n\tpublic RpcContext(RpcActionNode node, Request req) {\n\t\tthis.node = node;\n\t\tthis.req = req;\n\t}\n\n\tpublic Request req() {\n\t\treturn req;\n\t}\n\n\t@Override\n\tpublic RpcActionNode node() {\n\t\treturn this.node;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/RpcFilter.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server;\n\nimport java.util.Objects;\n\nimport org.yx.base.Ordered;\nimport org.yx.exception.SumkException;\n\npublic abstract class RpcFilter implements Ordered {\n\n\tprivate RpcFilter next;\n\n\tpublic final void setNext(RpcFilter next) {\n\t\tif (this.next != null) {\n\t\t\tthrow new SumkException(23431, \"next已经赋值了，它是\" + this.next);\n\t\t}\n\t\tthis.next = Objects.requireNonNull(next);\n\t}\n\n\tprotected final Object callNextFilter(RpcContext ctx) throws Throwable {\n\t\treturn this.next.doFilter(ctx);\n\t}\n\n\tpublic abstract Object doFilter(RpcContext ctx) throws Throwable;\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/ServerHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.concurrent.Executor;\n\nimport org.slf4j.Logger;\nimport org.yx.base.context.ActionContext;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SoaException;\nimport org.yx.log.Log;\nimport org.yx.main.SumkServer;\nimport org.yx.rpc.BusinessHandler;\nimport org.yx.rpc.RpcErrorCode;\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.context.InnerRpcUtil;\nimport org.yx.rpc.log.RpcLogs;\nimport org.yx.rpc.transport.TransportChannel;\n\npublic class ServerHandler implements BusinessHandler {\n\n\tprivate final Logger log;\n\tprivate final RequestHandler[] handlers;\n\tprivate final Executor executor;\n\n\tpublic ServerHandler(List<RequestHandler> handlers) {\n\t\tthis.log = Log.get(\"sumk.rpc.server\");\n\t\tthis.handlers = handlers.toArray(new RequestHandler[handlers.size()]);\n\t\tthis.executor = SumkServer.getExecutor(\"sumk.rpc.server.executor\");\n\t}\n\n\t@Override\n\tpublic void received(TransportChannel session, Object message) {\n\t\tthis.executor.execute(() -> {\n\t\t\tResponse resp = new Response();\n\t\t\tRequest req = null;\n\t\t\ttry {\n\t\t\t\tif (message == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (message instanceof Request) {\n\t\t\t\t\treq = (Request) message;\n\t\t\t\t\tInnerRpcUtil.rpcContext(req, req.isTest());\n\t\t\t\t\tfor (RequestHandler h : handlers) {\n\t\t\t\t\t\tif (h.handle(req, resp)) {\n\t\t\t\t\t\t\tresp.serviceInvokeMilTime(System.currentTimeMillis() - req.getStartInServer());\n\t\t\t\t\t\t\tsession.write(resp);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tLog.get(\"sumk.rpc.server\").warn(\"unkown message type:{}\", message.getClass().getName());\n\t\t\t} catch (Throwable e) {\n\t\t\t\tLog.get(\"sumk.rpc.server\").error(e.toString(), e);\n\t\t\t\tlong begin = 0;\n\t\t\t\tif (req != null) {\n\t\t\t\t\tbegin = req.getStartInServer();\n\t\t\t\t\tresp.sn(req.getSn());\n\t\t\t\t}\n\t\t\t\tresp.serviceInvokeMilTime(System.currentTimeMillis() - begin);\n\t\t\t\tresp.exception(SoaException.create(RpcErrorCode.SERVER_UNKNOW, \"server handler error\", e));\n\t\t\t\tsession.write(resp);\n\t\t\t} finally {\n\t\t\t\tRpcLogs.serverLog(req, resp);\n\t\t\t\tActionContext.remove();\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void exceptionCaught(TransportChannel session, Throwable cause) {\n\t\tsession.closeOnFlush();\n\t\tif (cause == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (cause.getClass() == IOException.class && AppInfo.getBoolean(\"rpc.session.print_simple_error\", true)) {\n\t\t\tString msg = cause.getMessage();\n\t\t\tif (msg != null && (msg.contains(\"连接\") || msg.contains(\"connection\"))) {\n\t\t\t\tlog.debug(\"session:{},message:{}\", session, cause.getMessage());\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tlog.error(session + \",message:\" + cause.getMessage(), cause);\n\t}\n\n\t@Override\n\tpublic void closed(TransportChannel session) {\n\t\tsession.closeNow();\n\n\t}\n}"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/SoaPlugin.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server;\n\nimport java.lang.reflect.Constructor;\nimport java.util.Collection;\n\nimport org.yx.annotation.Bean;\nimport org.yx.base.Lifecycle;\nimport org.yx.bean.IOC;\nimport org.yx.bean.InnerIOC;\nimport org.yx.bean.Plugin;\nimport org.yx.common.monitor.Monitors;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.main.SumkServer;\nimport org.yx.rpc.RpcSettings;\nimport org.yx.rpc.monitor.RpcMonitor;\nimport org.yx.rpc.registry.RegistryFactory;\nimport org.yx.rpc.server.impl.RpcHandler;\nimport org.yx.rpc.server.start.SoaAnnotationResolver;\nimport org.yx.rpc.transport.Transports;\nimport org.yx.util.Loader;\n\n@Bean\npublic class SoaPlugin implements Plugin {\n\n\tprotected Lifecycle server;\n\n\t@Override\n\tpublic void startAsync() {\n\n\t\tif (!SumkServer.isRpcEnable() || SumkServer.soaPort() < 0) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tRpcSettings.init();\n\t\t\tresolveSoaAnnotation(InnerIOC.beans());\n\t\t\tTransports.init();\n\t\t\tRpcHandler.init();\n\t\t\tMonitors.add(new RpcMonitor());\n\n\t\t\tRegistryFactory r = IOC.getFirstBean(RegistryFactory.class, false);\n\n\t\t\tString clzName = AppInfo.get(\"sumk.rpc.starter.class\", \"org.yx.rpc.server.start.SoaServer\");\n\t\t\tClass<?> clz = Loader.loadClass(clzName);\n\t\t\tConstructor<?> c = clz.getConstructor(RegistryFactory.class);\n\t\t\tserver = (Lifecycle) c.newInstance(r);\n\t\t} catch (Throwable e) {\n\t\t\tLog.printStack(\"sumk.error\", e);\n\t\t\tthrow new SumkException(35345436, \"rpc服务启动失败\");\n\t\t}\n\t}\n\n\tprotected void resolveSoaAnnotation(Collection<Object> beans) {\n\t\tSoaAnnotationResolver factory = new SoaAnnotationResolver();\n\t\ttry {\n\t\t\tfor (Object bean : beans) {\n\t\t\t\tfactory.resolve(bean);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow SumkException.wrap(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void afterStarted() {\n\t\tif (server != null) {\n\t\t\tserver.start();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tif (server != null) {\n\t\t\tserver.stop();\n\t\t\tserver = null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn 10000;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/impl/JsonedParamReqHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server.impl;\n\nimport org.yx.annotation.Bean;\nimport org.yx.rpc.RpcJson;\nimport org.yx.rpc.codec.ReqParamType;\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.context.RpcActionNode;\nimport org.yx.rpc.context.RpcActions;\nimport org.yx.rpc.server.RequestHandler;\nimport org.yx.rpc.server.Response;\nimport org.yx.rpc.server.RpcContext;\n\n@Bean\npublic class JsonedParamReqHandler implements RequestHandler {\n\n\t@Override\n\tpublic boolean handle(final Request req, Response resp) {\n\t\tif (!req.hasFeature(ReqParamType.REQ_PARAM_JSON)) {\n\t\t\treturn false;\n\t\t}\n\t\tresp.sn(req.getSn());\n\t\ttry {\n\t\t\tString api = req.getApi();\n\t\t\tRpcActionNode node = RpcActions.getActionNode(api);\n\t\t\tRpcActionNode.checkNode(api, node);\n\t\t\tRpcContext ctx = new RpcContext(node, req);\n\t\t\tctx.setParamPojo(node.createJsonParamPojo(req));\n\t\t\tObject ret = RpcHandler.handle(ctx);\n\t\t\tresp.json(RpcJson.server().toJson(ret));\n\t\t} catch (Throwable e) {\n\t\t\tServerExceptionHandler.handle(req, resp, e);\n\t\t}\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/impl/OrderedParamReqHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server.impl;\n\nimport org.yx.annotation.Bean;\nimport org.yx.rpc.RpcJson;\nimport org.yx.rpc.codec.ReqParamType;\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.context.RpcActionNode;\nimport org.yx.rpc.context.RpcActions;\nimport org.yx.rpc.server.RequestHandler;\nimport org.yx.rpc.server.Response;\nimport org.yx.rpc.server.RpcContext;\n\n@Bean\npublic class OrderedParamReqHandler implements RequestHandler {\n\n\t@Override\n\tpublic boolean handle(Request req, Response resp) {\n\t\tif (!req.hasFeature(ReqParamType.REQ_PARAM_ORDER)) {\n\t\t\treturn false;\n\t\t}\n\t\tresp.sn(req.getSn());\n\t\ttry {\n\t\t\tString api = req.getApi();\n\t\t\tRpcActionNode node = RpcActions.getActionNode(api);\n\t\t\tRpcActionNode.checkNode(api, node);\n\t\t\tRpcContext ctx = new RpcContext(node, req);\n\t\t\tctx.setParamPojo(node.createOrderParamPojo(req));\n\t\t\tObject ret = RpcHandler.handle(ctx);\n\t\t\tresp.json(RpcJson.server().toJson(ret));\n\t\t\tresp.exception(null);\n\t\t} catch (Throwable e) {\n\t\t\tServerExceptionHandler.handle(req, resp, e);\n\t\t}\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/impl/RpcHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server.impl;\n\nimport java.util.List;\n\nimport org.yx.bean.IOC;\nimport org.yx.rpc.server.LocalRpcContext;\nimport org.yx.rpc.server.RpcContext;\nimport org.yx.rpc.server.RpcFilter;\n\npublic final class RpcHandler {\n\n\tprivate static RpcFilter filter;\n\tprivate static final RpcFilter LAST = new RpcFilter() {\n\t\t@Override\n\t\tpublic Object doFilter(RpcContext ctx) throws Throwable {\n\t\t\treturn ctx.node().execute(ctx.getParamPojo());\n\t\t}\n\t};\n\n\tpublic static synchronized void init() {\n\t\tif (filter != null) {\n\t\t\treturn;\n\t\t}\n\t\tList<RpcFilter> list = IOC.getBeans(RpcFilter.class);\n\t\tif (list == null || list.isEmpty()) {\n\t\t\tfilter = LAST;\n\t\t\treturn;\n\t\t}\n\t\tfinal int size = list.size();\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tRpcFilter current = list.get(i);\n\t\t\tif (i == size - 1) {\n\t\t\t\tcurrent.setNext(LAST);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcurrent.setNext(list.get(i + 1));\n\t\t}\n\t\tfilter = list.get(0);\n\t}\n\n\tpublic static Object handle(RpcContext ctx) throws Throwable {\n\t\tRpcContext old = LocalRpcContext.getCtx();\n\t\tLocalRpcContext.setCtx(ctx);\n\t\ttry {\n\t\t\treturn filter.doFilter(ctx);\n\t\t} finally {\n\t\t\tif (old == null) {\n\t\t\t\tLocalRpcContext.remove();\n\t\t\t} else {\n\t\t\t\tLocalRpcContext.setCtx(old);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/impl/ServerExceptionHandler.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server.impl;\n\nimport org.yx.exception.SoaException;\nimport org.yx.log.Log;\nimport org.yx.rpc.RpcErrorCode;\nimport org.yx.rpc.RpcSettings;\nimport org.yx.rpc.codec.Request;\nimport org.yx.rpc.server.Response;\n\npublic final class ServerExceptionHandler {\n\n\tpublic static void handle(Request req, Response resp, Throwable e) {\n\t\tresp.json(null);\n\t\tresp.exception(SoaException.create(RpcErrorCode.SERVER_HANDLE_ERROR, e.getMessage(), e));\n\t\tif (RpcSettings.showServerExceptionLog()) {\n\t\t\tLog.get(\"sumk.rpc.log.server.exception\").error(e.toString(), e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaAnnotationResolver.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server.start;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.base.matcher.Matchers;\nimport org.yx.bean.BeanKit;\nimport org.yx.bean.aop.asm.AsmUtils;\nimport org.yx.bean.aop.asm.MethodParamInfo;\nimport org.yx.bean.aop.asm.ParamPojos;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\nimport org.yx.exception.SimpleSumkException;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.log.RawLog;\nimport org.yx.rpc.context.RpcActionNode;\nimport org.yx.rpc.context.RpcActions;\nimport org.yx.rpc.spec.RpcSpecs;\nimport org.yx.rpc.spec.SoaSpec;\nimport org.yx.util.Loader;\nimport org.yx.util.StringUtil;\n\npublic class SoaAnnotationResolver {\n\tprivate SoaClassResolver soaClassResolver;\n\tprivate SoaNameResolver nameResolver;\n\tprivate Predicate<String> exclude = BooleanMatcher.FALSE;\n\n\tpublic SoaAnnotationResolver() {\n\t\tnameResolver = newInstanceFromAppKey(\"sumk.rpc.name.resolver\");\n\t\tif (nameResolver == null) {\n\t\t\tnameResolver = new SoaNameResolverImpl();\n\t\t}\n\n\t\tsoaClassResolver = newInstanceFromAppKey(\"sumk.rpc.name.soaclass.resolver\");\n\t\tif (soaClassResolver == null) {\n\t\t\tsoaClassResolver = new SoaClassResolverImpl();\n\t\t}\n\n\t\tString patterns = AppInfo.get(\"sumk.rpc.server.exclude\", null);\n\t\tif (patterns != null) {\n\t\t\tthis.exclude = Matchers.createWildcardMatcher(patterns, 1);\n\t\t\tLogs.rpc().debug(\"soa server exclude:{}\", this.exclude);\n\t\t}\n\t}\n\n\tprivate void parseSoaClass(Map<Method, String> map, Class<?> targetClass, Class<?> refer)\n\t\t\tthrows NoSuchMethodException, SecurityException {\n\t\tString pre = this.soaClassResolver.solvePrefix(targetClass, refer);\n\t\tif (pre == null) {\n\t\t\treturn;\n\t\t}\n\t\tMethod[] methods = refer.getMethods();\n\t\tfor (Method m : methods) {\n\t\t\tif (m.getDeclaringClass() == Object.class) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tMethod methodInTarget = targetClass.getMethod(m.getName(), m.getParameterTypes());\n\t\t\tif (!m.getReturnType().isAssignableFrom(methodInTarget.getReturnType())) {\n\t\t\t\tthrow new SumkException(234324, targetClass.getName() + \".\" + methodInTarget.getName() + \"的返回值类型是\"\n\t\t\t\t\t\t+ methodInTarget.getReturnType().getName() + \",期待的类型是\" + m.getReturnType().getName());\n\t\t\t}\n\t\t\tmap.put(methodInTarget, pre);\n\t\t}\n\t}\n\n\tprivate void parseSoa(Map<Method, String> map, Class<?> clz) {\n\t\tMethod[] methods = clz.getMethods();\n\t\tfor (final Method m : methods) {\n\t\t\tif (RpcSpecs.extractSoa(m) == null || map.containsKey(m)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmap.putIfAbsent(m, \"\");\n\t\t}\n\t}\n\n\tpublic void resolve(Object bean) throws Exception {\n\n\t\tClass<?> clz = BeanKit.getTargetClass(bean);\n\t\tif (this.exclude.test(clz.getName())) {\n\t\t\treturn;\n\t\t}\n\t\tMap<Method, String> map = new HashMap<>();\n\n\t\tClass<?> refer = this.soaClassResolver.getRefer(clz, RpcSpecs.extractSoaClass(clz));\n\t\tif (refer != null) {\n\t\t\tthis.parseSoaClass(map, clz, refer);\n\t\t}\n\n\t\tif (refer != clz) {\n\t\t\tthis.parseSoa(map, clz);\n\t\t}\n\t\tList<Method> soaMethods = new ArrayList<>(map.size());\n\t\tfor (Method m : map.keySet()) {\n\t\t\tif (AsmUtils.notPublicOnly(m.getModifiers())) {\n\t\t\t\tLogs.asm().error(\"$$$ {}.{} has bad modifiers, maybe static or private\", clz.getName(), m.getName());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsoaMethods.add(m);\n\t\t}\n\t\tif (soaMethods.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tList<MethodParamInfo> mpInfos = AsmUtils.buildMethodInfos(soaMethods);\n\t\tfor (MethodParamInfo info : mpInfos) {\n\t\t\tMethod m = info.getMethod();\n\t\t\tString prefix = map.get(m);\n\t\t\tSoaSpec act = RpcSpecs.extractSoa(m);\n\t\t\tList<String> soaNames = StringUtil.isEmpty(prefix) ? nameResolver.solve(clz, m, act)\n\t\t\t\t\t: Collections.singletonList(prefix + m.getName());\n\t\t\tif (soaNames == null || soaNames.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tint toplimit = act != null && act.toplimit() > 0 ? act.toplimit()\n\t\t\t\t\t: AppInfo.getInt(\"sumk.rpc.toplimit.default\", Const.DEFAULT_TOPLIMIT);\n\t\t\tboolean publish = act != null ? act.publish() : true;\n\t\t\tRpcActionNode node = new RpcActionNode(bean, m, ParamPojos.create(info), toplimit, publish);\n\n\t\t\tfor (String soaName : soaNames) {\n\t\t\t\tif (soaName == null || soaName.isEmpty()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (RpcActions.getActionNode(soaName) != null) {\n\t\t\t\t\tRpcActionNode node0 = RpcActions.getActionNode(soaName);\n\t\t\t\t\tLogs.rpc().error(soaName + \" already existed -- {}.{},{}.{}\", node0.getDeclaringClass().getName(),\n\t\t\t\t\t\t\tnode0.getMethodName(), m.getDeclaringClass().getName(), m.getName());\n\t\t\t\t\tthrow new SimpleSumkException(1242436, soaName + \" already existed\");\n\t\t\t\t}\n\t\t\t\tRpcActions.putActNode(soaName, node);\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T> T newInstanceFromAppKey(String key) {\n\t\tString daoClz = AppInfo.get(key);\n\t\tif (daoClz != null && daoClz.length() > 2) {\n\t\t\ttry {\n\t\t\t\treturn (T) Loader.newInstance(daoClz);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tRawLog.error(\"sumk.bean\", e.getMessage(), e);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaClassResolver.java",
    "content": "package org.yx.rpc.server.start;\n\nimport org.yx.rpc.spec.SoaClassSpec;\n\npublic interface SoaClassResolver {\n\n\tClass<?> AUTO = Void.class;\n\n\tString solvePrefix(Class<?> targetClass, Class<?> refer);\n\n\tClass<?> getRefer(Class<?> targetClass, SoaClassSpec sc);\n\n}"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaClassResolverImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server.start;\n\nimport java.util.function.Predicate;\n\nimport org.yx.base.matcher.BooleanMatcher;\nimport org.yx.base.matcher.Matchers;\nimport org.yx.conf.AppInfo;\nimport org.yx.log.Logs;\nimport org.yx.rpc.context.InnerRpcUtil;\nimport org.yx.rpc.spec.SoaClassSpec;\nimport org.yx.util.Loader;\n\npublic class SoaClassResolverImpl implements SoaClassResolver {\n\n\tprivate Predicate<String> autoMatcher = BooleanMatcher.FALSE;\n\n\tpublic SoaClassResolverImpl() {\n\t\tString soaClassMatcher = AppInfo.get(\"sumk.rpc.intfserver.automatch\", null);\n\t\tif (soaClassMatcher != null) {\n\t\t\tthis.autoMatcher = Matchers.createWildcardMatcher(soaClassMatcher, 1);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String solvePrefix(Class<?> targetClass, Class<?> refer) {\n\t\tif (refer == null) {\n\t\t\tLogs.rpc().warn(\"{}的@SoaClass的值不能为null或Object\", targetClass.getName());\n\t\t\treturn null;\n\t\t}\n\t\tif (!refer.isAssignableFrom(targetClass)) {\n\t\t\tLogs.rpc().warn(\"{}的@SoaClass的value不是它的接口或超类\", targetClass.getName());\n\t\t\treturn null;\n\t\t}\n\t\treturn InnerRpcUtil.parseRpcIntfPrefix(refer);\n\t}\n\n\t@Override\n\tpublic Class<?> getRefer(Class<?> targetClass, SoaClassSpec sc) {\n\t\tClass<?> refer = parseRefer(targetClass, sc);\n\t\tif (refer != SoaClassResolver.AUTO) {\n\t\t\treturn refer;\n\t\t}\n\t\tClass<?>[] intfs = targetClass.getInterfaces();\n\t\tif (intfs != null && intfs.length == 1 && !intfs[0].getName().startsWith(Loader.JAVA_PRE)) {\n\t\t\treturn intfs[0];\n\t\t} else {\n\t\t\treturn targetClass;\n\t\t}\n\t}\n\n\tprotected Class<?> parseRefer(Class<?> targetClass, SoaClassSpec sc) {\n\t\tif (sc != null) {\n\t\t\treturn sc.refer();\n\t\t}\n\t\tif (this.autoMatcher.test(targetClass.getName())) {\n\t\t\treturn SoaClassResolver.AUTO;\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaNameResolver.java",
    "content": "package org.yx.rpc.server.start;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.yx.rpc.spec.SoaSpec;\n\npublic interface SoaNameResolver {\n\n\tList<String> solve(Class<?> clz, Method m, SoaSpec soa);\n\n}"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaNameResolverImpl.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server.start;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.yx.conf.AppInfo;\nimport org.yx.rpc.spec.SoaSpec;\nimport org.yx.util.StringUtil;\n\npublic class SoaNameResolverImpl implements SoaNameResolver {\n\n\tprivate final String appId;\n\n\tpublic SoaNameResolverImpl() {\n\t\tappId = AppInfo.getBoolean(\"sumk.rpc.appId.enable\", true) ? AppInfo.appId(null) : null;\n\t}\n\n\t@Override\n\tpublic List<String> solve(Class<?> clz, Method m, SoaSpec soa) {\n\t\tString soaName = soa.value();\n\t\tif (soaName == null || soaName.isEmpty()) {\n\t\t\tsoaName = m.getName();\n\t\t}\n\t\tString[] names = StringUtil.toLatin(soaName).split(\",\");\n\t\tList<String> list = new ArrayList<>(names.length);\n\t\tfor (String name : names) {\n\t\t\tname = name.trim();\n\t\t\tif (name.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlist.add(name);\n\t\t}\n\t\tif (list.isEmpty()) {\n\t\t\tlist.add(m.getName());\n\t\t}\n\t\tList<String> ret = new ArrayList<>(list.size());\n\t\tfor (String name : list) {\n\t\t\tret.add(solve(name, soa));\n\t\t}\n\t\treturn ret;\n\t}\n\n\tpublic String solve(String soaName, SoaSpec soa) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif (soa.appIdPrefix() && this.appId != null) {\n\t\t\tsb.append(this.appId).append('.');\n\t\t}\n\t\treturn sb.append(soaName).toString();\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.server.start;\n\nimport java.util.Optional;\n\nimport org.slf4j.Logger;\nimport org.yx.base.Lifecycle;\nimport org.yx.common.Host;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.log.Logs;\nimport org.yx.main.SumkServer;\nimport org.yx.rpc.registry.RegistryFactory;\nimport org.yx.rpc.registry.server.RegistryServer;\nimport org.yx.rpc.transport.TransportServer;\nimport org.yx.rpc.transport.Transports;\n\npublic class SoaServer implements Lifecycle {\n\n\tprivate volatile boolean started = false;\n\tprivate final Logger logger = Log.get(\"sumk.rpc.server\");\n\tprivate TransportServer server;\n\tprivate final Optional<RegistryServer> registry;\n\tprivate Host listenHost;\n\n\tpublic SoaServer(RegistryFactory registryFactory) {\n\t\tthis.registry = registryFactory.registryServer();\n\t}\n\n\tprotected Host startServer() throws Exception {\n\t\tString ip = SumkServer.soaHost();\n\t\tint port = SumkServer.soaPort();\n\t\tserver = Transports.factory().bind(ip, port);\n\t\tserver.start();\n\t\treturn Host.create(ip, server.getPort());\n\t}\n\n\t@Override\n\tpublic synchronized void stop() {\n\t\tif (this.registry.isPresent()) {\n\t\t\ttry {\n\t\t\t\tregistry.get().stop();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLogs.rpc().error(\"注册中心停止失败\", e);\n\t\t\t}\n\t\t}\n\n\t\tif (this.server != null) {\n\t\t\ttry {\n\t\t\t\tthis.server.stop();\n\t\t\t} catch (Exception e) {\n\t\t\t\tlogger.error(\"rpc的网络服务器停止失败\", e);\n\t\t\t}\n\t\t}\n\t\tstarted = false;\n\t}\n\n\t@Override\n\tpublic synchronized void start() {\n\t\tif (started) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tthis.listenHost = startServer();\n\t\t} catch (Exception e1) {\n\t\t\tlogger.error(\"soa端口监听失败\", e1);\n\t\t\tthrow new SumkException(35334545, \"soa服务启动失败\");\n\t\t}\n\t\tif (this.registry.isPresent()) {\n\t\t\ttry {\n\t\t\t\tthis.registry.get().start(getHostInRegistry());\n\t\t\t} catch (Exception e) {\n\t\t\t\tlogger.error(e.getLocalizedMessage(), e);\n\t\t\t\tthrow new SumkException(35334546, \"soa服务启动失败\");\n\t\t\t}\n\t\t} else {\n\t\t\tlogger.warn(\"注册中心没有配置，所以没有发起注册\");\n\t\t}\n\n\t\tstarted = true;\n\t}\n\n\t/**\n\t * 获取写到注册中心的地址\n\t * \n\t * @return 写到注册中心的地址\n\t */\n\tprotected Host getHostInRegistry() {\n\t\tString ip_zk = soaHostInRegistry();\n\t\tif (ip_zk == null) {\n\t\t\tip_zk = listenHost.ip();\n\t\t}\n\t\tint port_zk = soaPortInRegistry();\n\t\tif (port_zk < 1) {\n\t\t\tport_zk = listenHost.port();\n\t\t}\n\n\t\treturn Host.create(ip_zk, port_zk);\n\n\t}\n\n\tprivate String soaHostInRegistry() {\n\t\treturn AppInfo.get(\"sumk.rpc.registry.host\", null);\n\t}\n\n\tprivate int soaPortInRegistry() {\n\t\treturn AppInfo.getInt(\"sumk.rpc.registry.port\", -1);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/spec/RpcSpecs.java",
    "content": "package org.yx.rpc.spec;\n\nimport java.lang.reflect.Method;\nimport java.util.function.Function;\n\nimport org.yx.annotation.rpc.Soa;\nimport org.yx.annotation.rpc.SoaClass;\nimport org.yx.annotation.rpc.SoaClientConfig;\nimport org.yx.annotation.spec.Specs;\n\npublic class RpcSpecs extends Specs {\n\n\tprivate static Function<Method, SoaSpec> soaParser = m -> {\n\t\tSoa soa = m.getAnnotation(Soa.class);\n\t\tif (soa == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new SoaSpec(soa.value(), soa.cnName(), soa.appIdPrefix(), soa.toplimit(), soa.publish());\n\t};\n\n\tprivate static Function<Class<?>, SoaClassSpec> soaClassParser = clz -> {\n\t\tSoaClass c = clz.getAnnotation(SoaClass.class);\n\t\tif (c == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new SoaClassSpec(c.refer());\n\t};\n\n\tprivate static Function<Method, SoaClientConfigSpec> soaClientConfigParser = m -> {\n\t\tSoaClientConfig c = m.getAnnotation(SoaClientConfig.class);\n\t\tif (c == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new SoaClientConfigSpec(c.timeout(), c.tryCount());\n\t};\n\n\tpublic static SoaSpec extractSoa(Method m) {\n\t\treturn parse(m, soaParser);\n\t}\n\n\tpublic static SoaClassSpec extractSoaClass(Class<?> clz) {\n\t\treturn parse(clz, soaClassParser);\n\t}\n\n\tpublic static SoaClientConfigSpec extractSoaClientConfig(Method m) {\n\t\treturn parse(m, soaClientConfigParser);\n\t}\n\n\tpublic static Function<Method, SoaSpec> getSoaParser() {\n\t\treturn soaParser;\n\t}\n\n\tpublic static void setSoaParser(Function<Method, SoaSpec> soaParser) {\n\t\tRpcSpecs.soaParser = soaParser;\n\t}\n\n\tpublic static Function<Class<?>, SoaClassSpec> getSoaClassParser() {\n\t\treturn soaClassParser;\n\t}\n\n\tpublic static void setSoaClassParser(Function<Class<?>, SoaClassSpec> soaClassParser) {\n\t\tRpcSpecs.soaClassParser = soaClassParser;\n\t}\n\n\tpublic static Function<Method, SoaClientConfigSpec> getSoaClientConfigParser() {\n\t\treturn soaClientConfigParser;\n\t}\n\n\tpublic static void setSoaClientConfigParser(Function<Method, SoaClientConfigSpec> soaClientConfigParser) {\n\t\tRpcSpecs.soaClientConfigParser = soaClientConfigParser;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/spec/SoaClassSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.spec;\n\nimport java.util.Objects;\n\npublic class SoaClassSpec {\n\n\tprivate final Class<?> refer;\n\n\tpublic SoaClassSpec(Class<?> refer) {\n\t\tthis.refer = Objects.requireNonNull(refer);\n\t}\n\n\tpublic Class<?> refer() {\n\t\treturn this.refer;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/spec/SoaClientConfigSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.spec;\n\npublic class SoaClientConfigSpec {\n\n\tprivate final int timeout;\n\tprivate final int tryCount;\n\n\tpublic SoaClientConfigSpec(int timeout, int tryCount) {\n\t\tthis.timeout = timeout;\n\t\tthis.tryCount = tryCount;\n\t}\n\n\tpublic int timeout() {\n\t\treturn this.timeout;\n\t}\n\n\tpublic int tryCount() {\n\t\treturn this.tryCount;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/spec/SoaSpec.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.spec;\n\npublic class SoaSpec {\n\n\tprivate final String value;\n\tprivate final String cnName;\n\tprivate final boolean appIdPrefix;\n\tprivate final int toplimit;\n\tprivate final boolean publish;\n\n\tpublic SoaSpec(String value, String cnName, boolean appIdPrefix, int toplimit, boolean publish) {\n\t\tthis.value = value;\n\t\tthis.cnName = cnName;\n\t\tthis.appIdPrefix = appIdPrefix;\n\t\tthis.toplimit = toplimit;\n\t\tthis.publish = publish;\n\t}\n\n\tpublic String value() {\n\t\treturn this.value;\n\t}\n\n\tpublic String cnName() {\n\t\treturn this.cnName;\n\t}\n\n\tpublic boolean appIdPrefix() {\n\t\treturn this.appIdPrefix;\n\t}\n\n\tpublic int toplimit() {\n\t\treturn this.toplimit;\n\t}\n\n\tpublic boolean publish() {\n\t\treturn this.publish;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/transport/DataBuffer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.transport;\n\nimport org.yx.rpc.codec.DataStream;\n\npublic interface DataBuffer extends DataStream {\n\n\t/**\n\t * 写入byte数组\n\t * \n\t * @param bs 不能为null\n\t */\n\tvoid writeBytes(byte[] bs);\n\n\tvoid flip();\n\n\tboolean avilable(int length);\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/transport/RpcWriteFuture.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.transport;\n\npublic interface RpcWriteFuture {\n\tboolean isWritten();\n\n\tThrowable getException();\n\n\tvoid addListener(RpcWriteListener listener);\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/transport/RpcWriteListener.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.transport;\n\npublic interface RpcWriteListener {\n\tvoid afterWrited(RpcWriteFuture future);\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/transport/TransportChannel.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.transport;\n\nimport java.net.InetSocketAddress;\n\npublic interface TransportChannel {\n\tInetSocketAddress getRemoteAddress();\n\n\tRpcWriteFuture write(Object message);\n\n\tboolean isConnected();\n\n\tboolean isClosing();\n\n\tvoid closeNow();\n\n\tvoid closeOnFlush();\n\n\tObject getAttribute(String key);\n\n\tvoid setAttribute(String key, Object value);\n\n\tvoid removeAttribute(String key);\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/transport/TransportClient.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.transport;\n\nimport org.yx.common.Host;\nimport org.yx.rpc.client.Req;\n\npublic interface TransportClient {\n\n\tHost getRemoteAddr();\n\n\tRpcWriteFuture write(Req req);\n\n\tvoid closeIfPossibble();\n\n\tboolean isIdle();\n\n\tTransportChannel getChannel();\n\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/transport/TransportFactory.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.transport;\n\nimport org.yx.base.Ordered;\nimport org.yx.common.Host;\n\npublic interface TransportFactory extends Ordered {\n\n\t/**\n\t * 需要允许被多次调用\n\t */\n\tvoid initClient();\n\n\tTransportClient connect(Host serverAddr);\n\n\tTransportServer bind(String ip, int port);\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/transport/TransportServer.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.transport;\n\npublic interface TransportServer {\n\tvoid start();\n\n\tint getPort();\n\n\tvoid stop() throws Exception;\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/java/org/yx/rpc/transport/Transports.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.yx.rpc.transport;\n\nimport org.yx.base.context.AppContext;\nimport org.yx.bean.IOC;\nimport org.yx.log.Logs;\nimport org.yx.rpc.codec.CodecKit;\nimport org.yx.rpc.codec.decoders.DataDecoder;\nimport org.yx.rpc.codec.encoders.DataEncoder;\n\npublic class Transports {\n\tprivate static TransportFactory factory;\n\n\tpublic static TransportFactory factory() {\n\t\treturn factory;\n\t}\n\n\tpublic static void setFactory(TransportFactory factory) {\n\t\t_init(factory);\n\t}\n\n\tpublic static void init() {\n\t\t_init(IOC.getFirstBean(TransportFactory.class, false));\n\t}\n\n\tpublic static synchronized void _init(TransportFactory f) {\n\t\tif (factory != null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tCodecKit.init(IOC.getBeans(DataEncoder.class), IOC.getBeans(DataDecoder.class));\n\t\t\tfactory = f;\n\t\t} catch (Throwable e) {\n\t\t\tLogs.rpc().error(\"初始化rpc的TransportFactory失败\", e);\n\t\t\tAppContext.startFailed();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc/src/main/resources/META-INF/sumk.factories",
    "content": "sumk.ioc.bean=org.yx.rpc.client.intf.SoaClientFactory,org.yx.rpc.client.intf.SoaClientPlugin,\\\n\torg.yx.rpc.codec.decoders.RequestDecoder,org.yx.rpc.codec.decoders.ResponseDecoder,\\\n\torg.yx.rpc.codec.encoders.StreamAbleEncoder,org.yx.rpc.netty.NettyTransportFactory,\\\n\torg.yx.rpc.registry.zookeeper.ZkRegistryFactory,org.yx.rpc.server.impl.JsonedParamReqHandler,\\\n\torg.yx.rpc.server.impl.OrderedParamReqHandler,org.yx.rpc.server.SoaPlugin\nsumk.ioc.optional=org.yx.rpc.*"
  },
  {
    "path": "sumk-rpc-mina/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 youtongluan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "sumk-rpc-mina/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.github.youtongluan</groupId>\n\t\t<artifactId>sumk</artifactId>\n\t\t<version>4.2.1</version>\n\t</parent>\n\t<artifactId>sumk-rpc-mina</artifactId>\n\t<name>com.github.youtongluan:sumk</name>\n\t<description>A quick developing framewort for internet company</description>\n\t<url>https://github.com/youtongluan/sumk</url>\n\t<licenses>\n\t\t<license>\n\t\t\t<name>The Apache License, Version 2.0</name>\n\t\t\t<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n\t\t</license>\n\t</licenses>\n\t<scm>\n\t\t<url>https://github.com/youtongluan/sumk</url>\n\t\t<connection>https://github.com/youtongluan/sumk.git</connection>\n\t\t<developerConnection>https://github.com/youtongluan/sumk</developerConnection>\n\t</scm>\n\t<distributionManagement>\n\t    <repository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n\t    </repository>\n\t    <snapshotRepository>\n\t        <id>ossrh</id>\n\t        <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n\t    </snapshotRepository>\n\t</distributionManagement>\n\t<developers>\n\t\t<developer>\n\t\t\t<name>youtongluan</name>\n\t\t\t<email>3205207767@qq.com</email>\n\t\t\t<url>https://www.oschina.net/p/sumk</url>\n\t\t</developer>\n\t</developers>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>sumk-rpc</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.mina</groupId>\n\t\t\t<artifactId>mina-core</artifactId>\n\t\t\t<version>2.1.0</version>\n\t\t</dependency>\n\t\t\n\t\t\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>sumk-db</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n        \n\t</dependencies>\n\t\n</project>"
  },
  {
    "path": "sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaChannel.java",
    "content": "package org.yx.rpc.mina;\n\nimport java.net.InetSocketAddress;\n\nimport org.apache.mina.core.session.IoSession;\nimport org.yx.annotation.doc.NotNull;\nimport org.yx.rpc.transport.RpcWriteFuture;\nimport org.yx.rpc.transport.TransportChannel;\n\npublic class MinaChannel implements TransportChannel {\n\tprivate final IoSession session;\n\n\tprivate MinaChannel(@NotNull IoSession session) {\n\t\tthis.session = session;\n\t}\n\n\tpublic static MinaChannel create(IoSession session) {\n\t\tMinaChannel channel = (MinaChannel) session.getAttribute(TransportChannel.class.getName());\n\t\tif (channel == null) {\n\t\t\tchannel = new MinaChannel(session);\n\t\t\tchannel.setAttribute(TransportChannel.class.getName(), channel);\n\t\t}\n\t\treturn channel;\n\t}\n\n\t@Override\n\tpublic InetSocketAddress getRemoteAddress() {\n\t\treturn (InetSocketAddress) session.getRemoteAddress();\n\t}\n\n\t@Override\n\tpublic boolean isConnected() {\n\t\treturn session.isConnected();\n\t}\n\n\t@Override\n\tpublic void closeNow() {\n\t\tsession.closeNow();\n\t}\n\n\t@Override\n\tpublic void closeOnFlush() {\n\t\tsession.closeOnFlush();\n\t}\n\n\t@Override\n\tpublic Object getAttribute(String key) {\n\t\treturn session.getAttribute(key);\n\t}\n\n\t@Override\n\tpublic void setAttribute(String key, Object value) {\n\t\tsession.setAttribute(key, value);\n\t}\n\n\t@Override\n\tpublic void removeAttribute(String key) {\n\t\tsession.removeAttribute(key);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"mina:\" + session;\n\t}\n\n\t@Override\n\tpublic RpcWriteFuture write(Object message) {\n\t\treturn new MinaWriteFuture(session.write(message));\n\t}\n\n\t@Override\n\tpublic boolean isClosing() {\n\t\treturn session.isClosing();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((session == null) ? 0 : session.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tMinaChannel other = (MinaChannel) obj;\n\t\tif (session == null) {\n\t\t\tif (other.session != null)\n\t\t\t\treturn false;\n\t\t} else if (!session.equals(other.session))\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaClient.java",
    "content": "package org.yx.rpc.mina;\n\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Supplier;\n\nimport org.apache.mina.core.future.ConnectFuture;\nimport org.apache.mina.core.service.IoHandler;\nimport org.apache.mina.core.session.IoSession;\nimport org.apache.mina.filter.codec.ProtocolCodecFilter;\nimport org.apache.mina.transport.socket.SocketConnector;\nimport org.apache.mina.transport.socket.nio.NioSocketConnector;\nimport org.yx.common.Host;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Logs;\nimport org.yx.rpc.client.AbstractTransportClient;\nimport org.yx.rpc.client.ClientHandler;\nimport org.yx.rpc.transport.TransportClient;\n\npublic final class MinaClient extends AbstractTransportClient {\n\n\tprivate static Supplier<SocketConnector> connectorSupplier = new SocketConnectorSupplier();\n\n\tpublic static void setConnectorSupplier(Supplier<SocketConnector> connectorSupplier) {\n\t\tMinaClient.connectorSupplier = Objects.requireNonNull(connectorSupplier);\n\t}\n\n\tpublic static Supplier<SocketConnector> connectorSupplier() {\n\t\treturn connectorSupplier;\n\t}\n\n\tpublic MinaClient(Host host) {\n\t\tsuper(host);\n\t}\n\n\tprivate void connect(SocketConnector connector) throws InterruptedException {\n\n\t\tif (channel == null || channel.isClosing()) {\n\t\t\tLogs.rpc().debug(\"create session for {}\", addr);\n\t\t\tConnectFuture cf = connector.connect(addr.toInetSocketAddress());\n\n\t\t\tcf.await(connector.getConnectTimeoutMillis() + 20);\n\t\t\tIoSession se = cf.getSession();\n\t\t\tif (se != null) {\n\t\t\t\tthis.channel = MinaChannel.create(se);\n\t\t\t\tthis.channel.setAttribute(TransportClient.class.getName(), this);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcf.cancel();\n\t\t}\n\t}\n\n\t@Override\n\tprotected void connect() throws Exception {\n\t\tSocketConnector connector = connectorSupplier.get();\n\t\tif (lock.tryLock(connector.getConnectTimeoutMillis() + 2000, TimeUnit.MILLISECONDS)) {\n\t\t\ttry {\n\t\t\t\tif (channel != null && !channel.isClosing()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconnect(connector);\n\t\t\t} finally {\n\t\t\t\tlock.unlock();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class SocketConnectorSupplier implements Supplier<SocketConnector> {\n\n\t\tprivate volatile SocketConnector connector;\n\n\t\t@Override\n\t\tpublic SocketConnector get() {\n\t\t\tSocketConnector con = this.connector;\n\t\t\tif (con != null && !con.isDisposing() && !con.isDisposed()) {\n\t\t\t\treturn con;\n\t\t\t}\n\t\t\treturn this.create();\n\t\t}\n\n\t\tprivate synchronized SocketConnector create() {\n\t\t\tif (connector != null && !connector.isDisposing() && !connector.isDisposed()) {\n\t\t\t\treturn connector;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tNioSocketConnector con = new NioSocketConnector(\n\t\t\t\t\t\tAppInfo.getInt(\"sumk.rpc.client.poolsize\", Runtime.getRuntime().availableProcessors() + 1));\n\t\t\t\tcon.setConnectTimeoutMillis(AppInfo.getInt(\"sumk.rpc.connect.timeout\", 5000));\n\t\t\t\tMinaKit.config(con.getSessionConfig(), false);\n\t\t\t\tcon.setHandler(createClientHandler());\n\t\t\t\tcon.getFilterChain().addLast(\"codec\",\n\t\t\t\t\t\tnew ProtocolCodecFilter(new MinaProtocolEncoder(), new MinaProtocolDecoder()));\n\t\t\t\tthis.connector = con;\n\t\t\t\treturn con;\n\t\t\t} catch (Exception e) {\n\t\t\t\tLogs.rpc().error(e.getMessage(), e);\n\t\t\t\tthrow new SumkException(5423654, \"create connector error\", e);\n\t\t\t}\n\t\t}\n\n\t\tprotected IoHandler createClientHandler() {\n\t\t\treturn new MinaHandler(new ClientHandler());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaDataBuffer.java",
    "content": "package org.yx.rpc.mina;\n\nimport org.apache.mina.core.buffer.IoBuffer;\nimport org.yx.rpc.Profile;\nimport org.yx.rpc.codec.AbstractDataBuffer;\n\npublic class MinaDataBuffer extends AbstractDataBuffer {\n\n\tprivate final IoBuffer buf;\n\n\tpublic MinaDataBuffer(IoBuffer buf) {\n\t\tthis.buf = buf;\n\t}\n\n\tpublic IoBuffer buffer() {\n\t\treturn this.buf;\n\t}\n\n\t@Override\n\tpublic void writeBytes(byte[] bs) {\n\t\tbuf.put(bs);\n\t}\n\n\t@Override\n\tpublic void flip() {\n\t\tbuf.flip();\n\t}\n\n\t@Override\n\tpublic void position(int pos) {\n\t\tbuf.position(pos);\n\t}\n\n\t@Override\n\tpublic int position() {\n\t\treturn buf.position();\n\t}\n\n\t@Override\n\tpublic void read(byte[] dst, int offset, int length) {\n\t\tbuf.get(dst, offset, length);\n\t}\n\n\t@Override\n\tpublic void writePrefixedString(CharSequence s, int lengthBytes) throws Exception {\n\t\tif (s == null) {\n\t\t\ts = NULL;\n\t\t}\n\t\tthis.buf.putPrefixedString(s, lengthBytes, Profile.UTF8.newEncoder());\n\t}\n\n\t@Override\n\tpublic String readPrefixedString(int lengthBytes) throws Exception {\n\t\tString s = buf.getPrefixedString(lengthBytes, Profile.UTF8.newDecoder());\n\t\treturn NULL.equals(s) ? null : s;\n\t}\n\n\t@Override\n\tpublic void write(byte[] bs, int offset, int length) {\n\t\tbuf.get(bs, offset, length);\n\t}\n\n\t@Override\n\tpublic boolean avilable(int length) {\n\t\treturn buf.remaining() >= length;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaHandler.java",
    "content": "package org.yx.rpc.mina;\n\nimport java.util.Objects;\n\nimport org.apache.mina.core.service.IoHandler;\nimport org.apache.mina.core.session.IdleStatus;\nimport org.apache.mina.core.session.IoSession;\nimport org.apache.mina.filter.FilterEvent;\nimport org.yx.log.Logs;\nimport org.yx.rpc.BusinessHandler;\n\npublic class MinaHandler implements IoHandler {\n\n\tprivate final BusinessHandler handler;\n\n\tpublic MinaHandler(BusinessHandler handler) {\n\t\tthis.handler = Objects.requireNonNull(handler);\n\t}\n\n\t@Override\n\tpublic void sessionCreated(IoSession session) throws Exception {\n\t}\n\n\t@Override\n\tpublic void sessionOpened(IoSession session) throws Exception {\n\t\tLogs.rpc().debug(\"open session:{}-{}\", session.getServiceAddress(), session.getId());\n\t}\n\n\t@Override\n\tpublic void sessionClosed(IoSession session) throws Exception {\n\t\tthis.handler.closed(MinaChannel.create(session));\n\t}\n\n\t@Override\n\tpublic void sessionIdle(IoSession session, IdleStatus status) throws Exception {\n\t\tlong time = System.currentTimeMillis() - session.getLastIoTime();\n\t\tLogs.rpc().info(\"rpc session {} for {}ms,closed by this server\", session, time);\n\t\tsession.closeOnFlush();\n\t}\n\n\t@Override\n\tpublic void exceptionCaught(IoSession session, Throwable cause) throws Exception {\n\t\tthis.handler.exceptionCaught(MinaChannel.create(session), cause);\n\t}\n\n\t@Override\n\tpublic void messageReceived(IoSession session, Object message) throws Exception {\n\t\tthis.handler.received(MinaChannel.create(session), message);\n\t}\n\n\t@Override\n\tpublic void messageSent(IoSession session, Object message) throws Exception {\n\n\t}\n\n\t@Override\n\tpublic void inputClosed(IoSession session) throws Exception {\n\t\tsession.closeNow();\n\t}\n\n\t@Override\n\tpublic void event(IoSession arg0, FilterEvent arg1) throws Exception {\n\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaKit.java",
    "content": "package org.yx.rpc.mina;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.mina.core.session.IdleStatus;\nimport org.apache.mina.transport.socket.SocketSessionConfig;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.SimpleBeanUtil;\nimport org.yx.log.Logs;\nimport org.yx.rpc.RpcSettings;\n\npublic final class MinaKit {\n\n\tpublic static void config(SocketSessionConfig conf, boolean server) {\n\n\t\tlong maxIdle = server ? RpcSettings.maxServerIdleTime() : RpcSettings.maxClientIdleTime();\n\t\tint maxIdleSecond = (int) (maxIdle / 1000);\n\t\tLogs.rpc().debug(\"max idel time for server:{} is {} second\", server, maxIdleSecond);\n\t\tconf.setIdleTime(IdleStatus.BOTH_IDLE, maxIdleSecond);\n\t\tMap<String, String> map = new HashMap<>(AppInfo.subMap(\"sumk.rpc.conf.\"));\n\t\tString selfKey = server ? \"sumk.rpc.server.conf.\" : \"sumk.rpc.client.conf.\";\n\t\tmap.putAll(AppInfo.subMap(selfKey));\n\t\tif (map.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tString flag = server ? \"server\" : \"client\";\n\t\tLogs.rpc().info(flag + \" session config: {}\", map);\n\t\ttry {\n\t\t\tSimpleBeanUtil.copyProperties(conf, map);\n\t\t} catch (Exception e) {\n\t\t\tLogs.rpc().warn(flag + \" rpc config error. \" + e.getMessage(), e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaProtocolDecoder.java",
    "content": "package org.yx.rpc.mina;\n\nimport java.nio.charset.CharacterCodingException;\n\nimport org.apache.mina.core.buffer.IoBuffer;\nimport org.apache.mina.core.session.IoSession;\nimport org.apache.mina.filter.codec.CumulativeProtocolDecoder;\nimport org.apache.mina.filter.codec.ProtocolDecoderException;\nimport org.apache.mina.filter.codec.ProtocolDecoderOutput;\nimport org.yx.log.Logs;\nimport org.yx.rpc.codec.CodecKit;\n\npublic class MinaProtocolDecoder extends CumulativeProtocolDecoder {\n\n\tprotected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)\n\t\t\tthrows CharacterCodingException, ProtocolDecoderException {\n\n\t\ttry {\n\n\t\t\tObject message = CodecKit.decode(new MinaDataBuffer(in));\n\t\t\tif (message == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tout.write(message);\n\t\t} catch (Exception e) {\n\t\t\tLogs.rpc().error(\"数据解析出错\", e);\n\t\t\tthrow new ProtocolDecoderException(\"数据解析出错,\" + e.getMessage());\n\t\t}\n\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaProtocolEncoder.java",
    "content": "package org.yx.rpc.mina;\n\nimport java.nio.ByteOrder;\n\nimport org.apache.mina.core.buffer.IoBuffer;\nimport org.apache.mina.core.session.IoSession;\nimport org.apache.mina.filter.codec.ProtocolEncoder;\nimport org.apache.mina.filter.codec.ProtocolEncoderOutput;\nimport org.yx.rpc.codec.CodecKit;\n\npublic class MinaProtocolEncoder implements ProtocolEncoder {\n\n\t@Override\n\tpublic void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {\n\t\tif (message == null) {\n\t\t\treturn;\n\t\t}\n\t\tMinaDataBuffer buf = new MinaDataBuffer(createIoBuffer(2048));\n\t\tCodecKit.encode(message, buf);\n\t\tIoBuffer ioBuf = buf.buffer();\n\t\tif (ioBuf.remaining() > 0) {\n\t\t\tout.write(ioBuf);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dispose(IoSession session) throws Exception {\n\n\t}\n\n\tprivate static IoBuffer createIoBuffer(int strLength) {\n\t\treturn IoBuffer.allocate(strLength).setAutoExpand(true).order(ByteOrder.BIG_ENDIAN);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaServer.java",
    "content": "package org.yx.rpc.mina;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.concurrent.locks.LockSupport;\n\nimport org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;\nimport org.apache.mina.core.service.IoHandler;\nimport org.apache.mina.filter.codec.ProtocolCodecFilter;\nimport org.apache.mina.transport.socket.SocketAcceptor;\nimport org.apache.mina.transport.socket.nio.NioSocketAcceptor;\nimport org.yx.bean.IOC;\nimport org.yx.conf.AppInfo;\nimport org.yx.exception.SumkException;\nimport org.yx.log.Log;\nimport org.yx.rpc.server.RequestHandler;\nimport org.yx.rpc.server.ServerHandler;\nimport org.yx.rpc.transport.TransportServer;\n\npublic class MinaServer implements TransportServer {\n\n\tprivate final String host;\n\tprivate int port;\n\tprivate IoHandler handler;\n\tprivate int acceptors;\n\tprivate SocketAcceptor acceptor;\n\n\tpublic void setAcceptors(int acceptors) {\n\t\tthis.acceptors = acceptors;\n\t}\n\n\tpublic MinaServer(String host, int port) {\n\t\tthis.port = port;\n\t\tthis.host = host;\n\t\tthis.handler = createServerHandler();\n\t}\n\n\tprotected IoHandler createServerHandler() {\n\t\treturn new MinaHandler(new ServerHandler(IOC.getBeans(RequestHandler.class)));\n\t}\n\n\tprotected InetSocketAddress listenAddr(boolean randomPort) {\n\t\tif (randomPort) {// 1万到6万之间\n\t\t\tint start = AppInfo.getInt(\"sumk.rpc.port.start\", 10000);\n\t\t\tint end = AppInfo.getInt(\"sumk.rpc.port.end\", 60000);\n\t\t\tport = start + ThreadLocalRandom.current().nextInt(end - start);\n\t\t}\n\t\tif (host == null || host.trim().length() == 0) {\n\t\t\treturn new InetSocketAddress(port);\n\t\t}\n\t\treturn new InetSocketAddress(host, port);\n\t}\n\n\tpublic synchronized void start() {\n\t\tif (acceptor != null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tacceptor = acceptors > 0 ? new NioSocketAcceptor(acceptors) : new NioSocketAcceptor();\n\t\t\tacceptor.setReuseAddress(AppInfo.getBoolean(\"sumk.rpc.port.reuse\", false));\n\t\t\tDefaultIoFilterChainBuilder chain = acceptor.getFilterChain();\n\n\t\t\tchain.addLast(\"codec\", new ProtocolCodecFilter(new MinaProtocolEncoder(), new MinaProtocolDecoder()));\n\n\t\t\tacceptor.setHandler(handler);\n\t\t\tMinaKit.config(acceptor.getSessionConfig(), true);\n\t\t\tboolean randomPort = this.port < 1;\n\t\t\tfor (int i = 0; i < 50; i++) {\n\t\t\t\ttry {\n\t\t\t\t\tInetSocketAddress addr = listenAddr(randomPort);\n\t\t\t\t\tacceptor.bind(addr);\n\t\t\t\t\tLog.get(\"sumk.rpc.server\").info(\"rpc(mina) listening on \" + addr);\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tif (randomPort) {\n\t\t\t\t\t\tLog.get(\"sumk.rpc.server\").info(\"{} was occupied,try another port...\", this.port);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tLog.get(\"sumk.rpc.server\").debug(\"waiting for listening to {}.{}\", port, e.getMessage());\n\t\t\t\t\tint time = AppInfo.getInt(\"sumk.rpc.server.starting.sleep\", 5000);\n\t\t\t\t\tLockSupport.parkUntil(System.currentTimeMillis() + time);\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tLog.get(\"sumk.rpc.server\").debug(e.getLocalizedMessage(), e);\n\t\t\tacceptor = null;\n\t\t\tthrow new SumkException(38057306, \"start mina server failed\", e);\n\t\t}\n\n\t}\n\n\tpublic int getPort() {\n\t\treturn port;\n\t}\n\n\tpublic void stop() throws IOException {\n\t\tif (this.acceptor == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.acceptor.dispose(false);\n\t\tthis.acceptor = null;\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaTransportFactory.java",
    "content": "package org.yx.rpc.mina;\n\nimport org.yx.annotation.Bean;\nimport org.yx.common.Host;\nimport org.yx.rpc.transport.TransportClient;\nimport org.yx.rpc.transport.TransportFactory;\nimport org.yx.rpc.transport.TransportServer;\n\n@Bean\npublic class MinaTransportFactory implements TransportFactory {\n\n\t@Override\n\tpublic TransportClient connect(Host serverAddr) {\n\t\treturn new MinaClient(serverAddr);\n\t}\n\n\t@Override\n\tpublic TransportServer bind(String ip, int port) {\n\t\treturn new MinaServer(ip, port);\n\t}\n\n\t@Override\n\tpublic void initClient() {\n\t\tMinaClient.connectorSupplier().get();\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaWriteFuture.java",
    "content": "package org.yx.rpc.mina;\n\nimport java.util.Objects;\n\nimport org.apache.mina.core.future.IoFutureListener;\nimport org.apache.mina.core.future.WriteFuture;\nimport org.yx.rpc.transport.RpcWriteFuture;\nimport org.yx.rpc.transport.RpcWriteListener;\n\npublic class MinaWriteFuture implements RpcWriteFuture {\n\tprivate final WriteFuture future;\n\n\tpublic MinaWriteFuture(WriteFuture future) {\n\t\tthis.future = Objects.requireNonNull(future);\n\t}\n\n\t@Override\n\tpublic boolean isWritten() {\n\t\treturn future.isWritten();\n\t}\n\n\t@Override\n\tpublic Throwable getException() {\n\t\treturn future.getException();\n\t}\n\n\t@Override\n\tpublic void addListener(RpcWriteListener listener) {\n\t\tfuture.addListener(new MinaWriteListener(Objects.requireNonNull(listener), this));\n\t}\n\n\tprivate static final class MinaWriteListener implements IoFutureListener<WriteFuture> {\n\t\tprivate final RpcWriteListener listener;\n\t\tprivate final MinaWriteFuture writeFuture;\n\n\t\tpublic MinaWriteListener(RpcWriteListener listener, MinaWriteFuture writeFuture) {\n\t\t\tthis.listener = listener;\n\t\t\tthis.writeFuture = writeFuture;\n\t\t}\n\n\t\t@Override\n\t\tpublic void operationComplete(WriteFuture future) {\n\t\t\tlistener.afterWrited(writeFuture);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/main/resources/META-INF/sumk.factories",
    "content": "sumk.ioc.bean=org.yx.rpc.mina.MinaTransportFactory\nsumk.ioc.exclude=org.yx.rpc.netty.NettyTransportFactory"
  },
  {
    "path": "sumk-rpc-mina/src/test/java/org/test/Main.java",
    "content": "package org.test;\n\nimport org.yx.log.Log;\nimport org.yx.main.SumkServer;\n\npublic class Main {\n\tpublic static void main(String[] args) {\n\t\ttry {\n\t\t\tLog.get(Main.class).info(\"需要在外部启动一个zookeeper服务器\");\n\t\t\tlong begin=System.currentTimeMillis();\n\t\t\tSumkServer.start(Main.class,null);\n\t\t\tSystem.out.println(\"启动耗时：\"+(System.currentTimeMillis()-begin)+\"毫秒\");\n\t\t\tThread.currentThread().join();\n\t\t} catch (Exception e) {\n\t\t\tLog.printStack(\"main\",e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/test/java/org/test/RpcPressTest.java",
    "content": "package org.test;\n\nimport java.util.Arrays;\nimport java.util.Random;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.yx.log.LogLevel;\nimport org.yx.log.Loggers;\nimport org.yx.main.StartConstants;\nimport org.yx.main.SumkServer;\nimport org.yx.rpc.client.LockHolder;\nimport org.yx.rpc.client.Rpc;\nimport org.yx.util.SumkDate;\n\npublic class RpcPressTest {\n\n\t@Before\n\tpublic void before(){\n\t\tSumkServer.start(RpcPressTest.class,Arrays.asList(StartConstants.NOHTTP,StartConstants.NOSOA));\n\t\tLoggers.setDefaultLevel(LogLevel.ERROR);//这个只能修改默认级别的，如果有具体设置了日志级别，它的优先级比这个高\n\t\tRpc.init();\n\t}\n\t\n\t@After\n\tpublic void after(){\n\t\tAssert.assertEquals(0, LockHolder.lockSize());\n\t\tSystem.out.println(\"锁状态正确！\");\n\t}\n\t\n\tRandom r=new Random();\n\t\n\t@Test\n\tpublic void test() throws InterruptedException {\n\t\tSystem.out.println(\"开始压测，请耐心等待30秒左右。。。\");\n\t\tfinal int count=200000;\n\t\tfinal int threadCount=50;\n\t\tAtomicInteger failCount=new AtomicInteger();\n\t\tCountDownLatch down=new CountDownLatch(count);\n\t\tRpc.call(\"a.b.repeat\", \"预热\");\n\t\tchar[] cs=new char[1200];\n\t\tArrays.fill(cs, 't');\n\t\tArrays.fill(cs, 100,200,'好');\n\t\tcs[5]='啊';\n\t\tcs[45]='1';\n\t\tcs[59]='o';\n\t\tcs[115]='是';\n\t\tcs[800]='是';\n\t\tString pre=new String(cs);\n\t\tlong begin=System.currentTimeMillis();\n\t\tAtomicLong totalRT=new AtomicLong();\n\t\tfor(int i=0;i<threadCount;i++){\n\t\t\tnew Thread(){\n\t\t\t\t@Override\n\t\t\t\tpublic void run(){\n\t\t\t\t\tlong t=0;\n\t\t\t\t\tfor(int i=0;i<count/threadCount;i++){\n\t\t\t\t\t\t//收发1k左右的数据量\n\t\t\t\t\t\tString msg=pre+i;\n\t\t\t\t\t\tlong b2=System.currentTimeMillis();\n\t\t\t\t\t\tString msg2=Rpc.create(\"a.b.repeat\").paramInArray(msg).timeout(30000).execute().getOrException();\n\t\t\t\t\t\tt+=System.currentTimeMillis()-b2;\n\t\t\t\t\t\tif(!(\"\\\"\"+msg+\"\\\"\").equals(msg2)){\n\t\t\t\t\t\t\tfailCount.incrementAndGet();\n\t\t\t\t\t\t\tSystem.out.println(msg);\n\t\t\t\t\t\t\tSystem.out.println(msg2);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdown.countDown();\n\t\t\t\t\t}\n\t\t\t\t\ttotalRT.addAndGet(t);\n\t\t\t\t}\n\t\t\t}.start();\n\t\t}\n\t\twhile(!down.await(5, TimeUnit.SECONDS)){\n\t\t\tSystem.out.println(SumkDate.now()+\" - 剩余请求：\"+down.getCount());\n\t\t}\n\t\tlong time=System.currentTimeMillis()-begin;\n\t\tSystem.out.println(\"耗时：\"+time+\",平均每秒请求数：\"+(count*1000d/time));\n\t\tSystem.out.println(\"平均每个响应耗时：\"+totalRT.get()/count+\"ms\");\n\t\tAssert.assertEquals(0, failCount.get());\n\t}\n\t\n\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/test/java/org/test/RpcTest.java",
    "content": "package org.test;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\n\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.test.soa.demo.EchoAction;\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.main.StartConstants;\nimport org.yx.main.SumkServer;\nimport org.yx.rpc.client.Rpc;\nimport org.yx.rpc.client.RpcFuture;\n\n// RPC是异步启动的，所以有可能打印启动成功，但实际上还没有\npublic class RpcTest {\n\n\t@BeforeClass\n\tpublic static void before() {\n\t\tSumkServer.start(RpcPressTest.class,Arrays.asList(StartConstants.NOHTTP,StartConstants.NOSOA));\n\t\tRpc.init();\n\t}\n\n\tpublic static String soaName(String soaName){\n\t\tStringBuilder sb=new StringBuilder();\n\t\tif(AppInfo.appId(null)!=null){\n\t\t\tsb.append(AppInfo.appId(null)).append('.');\n\t\t}\n\t\treturn sb.append(soaName).toString();\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tList<String> names = Arrays.asList(\"游夏\", \"游侠\");\n\t\tString echo = \"how are you\";\n\t\t// ret是json格式\n\t\tString ret = Rpc.call(soaName(\"echo\"), echo, names);\n\t\tSystem.out.println(\"result:\" + ret);\n\t\tAssert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class));\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tMap<String, Object> map = new HashMap<>();\n\t\t\tmap.put(\"echo\", echo);\n\t\t\tmap.put(\"names\", names);\n\t\t\tret = Rpc.callInMap(soaName(\"echo\"), map);\n\t\t\tAssert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class));\n\t\t\tret = Rpc.call(soaName(\"echo\"), echo, names);\n\t\t\tAssert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class));\n\t\t\tSystem.out.println(\"test:\" + ret);\n\t\t}\n\t}\n\n\tRandom r = new Random();\n\n\n\t@Test\n\tpublic void async() {\n\t\tSystem.out.println(\"now:\" + System.currentTimeMillis());\n\t\tList<String> names = Arrays.asList(\"游夏\", \"游侠\");\n\t\tString echo = \"how are you,\";\n\t\t// ret是json格式\n\t\tList<RpcFuture> retList = new ArrayList<>();\n\t\tList<RpcFuture> retList2 = new ArrayList<>();\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tMap<String, Object> map = new HashMap<>();\n\t\t\tmap.put(\"echo\", echo + i);\n\t\t\tmap.put(\"names\", names);\n\t\t\tretList.add(Rpc.callInMapAsync(soaName(\"echo\"), map));\n\t\t\tretList2.add(Rpc.callAsync(soaName(\"echo\"), echo + i, names));\n\t\t}\n\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tSystem.out.println(i + \" 异步\");\n\t\t\tAssert.assertEquals(new EchoAction().echo(echo + i, names),\n\t\t\t\t\tS.json().fromJson(retList.get(i).opt(), List.class));\n\t\t\tAssert.assertEquals(new EchoAction().echo(echo + i, names),\n\t\t\t\t\tS.json().fromJson(retList2.get(i).opt(), List.class));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/test/java/org/test/inner/pojo/DemoUser.java",
    "content": "package org.test.inner.pojo;\n\nimport java.util.Date;\n\nimport org.yx.annotation.db.Column;\nimport org.yx.annotation.db.SoftDelete;\nimport org.yx.annotation.db.Table;\nimport org.yx.db.enums.ColumnType;\nimport org.yx.util.SumkDate;\n\n@Table\n@SoftDelete(value = \"enable\", type = Byte.class)\npublic class DemoUser {\n\n\t@Column(type = ColumnType.ID_BOTH)\n\tprivate Long id;\n\tprivate String name;\n\tprivate Integer age;\n\tprivate Date lastUpdate;\n\n\tpublic Date getLastUpdate() {\n\t\treturn lastUpdate;\n\t}\n\n\tpublic void setLastUpdate(Date lastUpdate) {\n\t\tthis.lastUpdate = lastUpdate;\n\t}\n\n\tpublic Long getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(Long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic Integer getAge() {\n\t\treturn age;\n\t}\n\n\tpublic void setAge(Integer age) {\n\t\tthis.age = age;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DemoUser [id=\" + id + \", name=\" + name + \", age=\" + age + \", lastUpdate=\"\n\t\t\t\t+ (lastUpdate==null ? \"null\" : SumkDate.of(lastUpdate)).toString() + \"]\";\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((age == null) ? 0 : age.hashCode());\n\t\tresult = prime * result + ((id == null) ? 0 : id.hashCode());\n\t\tresult = prime * result + ((name == null) ? 0 : name.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tDemoUser other = (DemoUser) obj;\n\t\tif (age == null) {\n\t\t\tif (other.age != null)\n\t\t\t\treturn false;\n\t\t} else if (!age.equals(other.age))\n\t\t\treturn false;\n\t\tif (id == null) {\n\t\t\tif (other.id != null)\n\t\t\t\treturn false;\n\t\t} else if (!id.equals(other.id))\n\t\t\treturn false;\n\t\tif (name == null) {\n\t\t\tif (other.name != null)\n\t\t\t\treturn false;\n\t\t} else if (!name.equals(other.name))\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/test/java/org/test/inner/pojo/Multikey.java",
    "content": "package org.test.inner.pojo;\n\nimport org.yx.annotation.db.Column;\nimport org.yx.annotation.db.Table;\nimport org.yx.db.enums.ColumnType;\n\n@Table\npublic class Multikey {\n\n\t@Column(type = ColumnType.ID_BOTH, order = 1)\n\tprivate String id1;\n\t@Column(type = ColumnType.ID_BOTH, order = 2)\n\tprivate String id2;\n\tprivate String name;\n\tprivate Integer age;\n\n\tpublic String getId1() {\n\t\treturn id1;\n\t}\n\n\tpublic Multikey setId1(String id1) {\n\t\tthis.id1 = id1;\n\t\treturn this;\n\t}\n\n\tpublic String getId2() {\n\t\treturn id2;\n\t}\n\n\tpublic Multikey setId2(String id2) {\n\t\tthis.id2 = id2;\n\t\treturn this;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic Multikey setName(String name) {\n\t\tthis.name = name;\n\t\treturn this;\n\t}\n\n\tpublic Integer getAge() {\n\t\treturn age;\n\t}\n\n\tpublic Multikey setAge(Integer age) {\n\t\tthis.age = age;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Multikey [id1=\" + id1 + \", id2=\" + id2 + \", name=\" + name + \", age=\" + age + \"]\";\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((age == null) ? 0 : age.hashCode());\n\t\tresult = prime * result + ((id1 == null) ? 0 : id1.hashCode());\n\t\tresult = prime * result + ((id2 == null) ? 0 : id2.hashCode());\n\t\tresult = prime * result + ((name == null) ? 0 : name.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tMultikey other = (Multikey) obj;\n\t\tif (age == null) {\n\t\t\tif (other.age != null)\n\t\t\t\treturn false;\n\t\t} else if (!age.equals(other.age))\n\t\t\treturn false;\n\t\tif (id1 == null) {\n\t\t\tif (other.id1 != null)\n\t\t\t\treturn false;\n\t\t} else if (!id1.equals(other.id1))\n\t\t\treturn false;\n\t\tif (id2 == null) {\n\t\t\tif (other.id2 != null)\n\t\t\t\treturn false;\n\t\t} else if (!id2.equals(other.id2))\n\t\t\treturn false;\n\t\tif (name == null) {\n\t\t\tif (other.name != null)\n\t\t\t\treturn false;\n\t\t} else if (!name.equals(other.name))\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n}"
  },
  {
    "path": "sumk-rpc-mina/src/test/java/org/test/soa/demo/EchoAction.java",
    "content": "package org.test.soa.demo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.rpc.Soa;\n\n@Bean\npublic class EchoAction {\n\n\t@Soa(\"a.b.repeat\")\n\tpublic String repeat(String s){\n\t\treturn s;\n\t}\n\t\n\t@Soa\n\tpublic List<String> echo(String echo, List<String> names) {\n\n\t\tList<String> list = new ArrayList<String>();\n\t\tfor (String name : names) {\n\t\t\tlist.add(echo + \" \" + name);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Soa\n\tpublic String hi() {\n\t\treturn \"hello\";\n\t}\n\n\t@Soa\n\tpublic void print() {\n\t\tSystem.out.println(\"print\");\n\t}\n\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/test/java/org/test/soa/server/SOAServer.java",
    "content": "package org.test.soa.server;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\n\nimport org.apache.zookeeper.server.NIOServerCnxnFactory;\nimport org.apache.zookeeper.server.ZooKeeperServer;\nimport org.yx.util.UUIDSeed;\n\npublic class SOAServer {\n\n\tprivate static ZooKeeperServer zk;\n\tprivate static NIOServerCnxnFactory nioFactory;\n\tpublic static void startZKServer() throws IOException, InterruptedException{\n\t\tstartZKServer(500,2000);\n\t}\n\t\n\tpublic static void stopZKServer(){\n\t\tif(zk!=null){\n\t\t\tzk.shutdown();\n\t\t}\n\t}\n\t\n\tpublic static void startZKServer(int tickTime, int minSessionTimeout) throws IOException, InterruptedException{\n\t\tString temp=System.getProperty(\"java.io.tmpdir\");\n\t\tFile f=new File(temp,UUIDSeed.seq());\n\t\tf.mkdir();\n\t\t\n\t\t\n\t\tzk = new ZooKeeperServer(f, f, tickTime);\n        zk.setMinSessionTimeout(minSessionTimeout);\n        nioFactory = new NIOServerCnxnFactory();\n        int maxClientConnections = 0; // 0 means unlimited\n        nioFactory.configure(new InetSocketAddress(\"127.0.0.1\",2181), maxClientConnections);\n        nioFactory.startup(zk);\n\t}\n}\n"
  },
  {
    "path": "sumk-rpc-mina/src/test/resources/app.properties",
    "content": "sumk.zkurl=127.0.0.1:2181\n\n#used in RPC\nsumk.rpc.port=0\n\n\n\n\n"
  },
  {
    "path": "sumk-test/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 youtongluan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "sumk-test/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.github.youtongluan</groupId>\n\t\t<artifactId>sumk</artifactId>\n\t\t<version>4.2.1</version>\n\t</parent>\n\t<artifactId>sumk-test</artifactId>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>sumk-rpc</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>sumk-http</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>sumk-db</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>com.github.youtongluan</groupId>\n\t\t\t<artifactId>async-logger</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.httpcomponents</groupId>\n\t\t\t<artifactId>httpasyncclient</artifactId>\n\t\t\t<scope>test</scope>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<artifactId>commons-logging</artifactId>\n\t\t\t\t\t<groupId>commons-logging</groupId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.httpcomponents</groupId>\n\t\t\t<artifactId>httpmime</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n        \n\t</dependencies>\n\t\n</project>"
  },
  {
    "path": "sumk-test/src/test/java/org/test/Main.java",
    "content": "package org.test;\n\nimport org.yx.log.Log;\nimport org.yx.main.SumkServer;\n\npublic class Main {\n\tpublic static void main(String[] args) {\n\t\ttry {\n\t\t\t// 新版zk不好启动服务端，需要使用外置zk服务器\n//\t\t\tLog.get(Main.class).info(\"为了测试方便，测试环境内置了zookeeper服务器。\");\n//\t\t\tLog.get(Main.class).info(\"现在开始启动内置zookeeper。。。\");\n//\t\t\tSOAServer.startZKServer();\n//\t\t\tLog.get(Main.class).info(\"zookeeper启动完成，现在开始启动真正的sumk服务器。。。\");\n\t\t\tlong begin=System.currentTimeMillis();\n\t\t\tSumkServer.start(Main.class,null);\n\t\t\tSystem.out.println(\"启动耗时：\"+(System.currentTimeMillis()-begin)+\"毫秒\");\n\t\t\tThread.currentThread().join();\n\t\t} catch (Exception e) {\n\t\t\tLog.printStack(\"main\",e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/client/HttpAesClientTest.java",
    "content": "package org.test.client;\n\nimport java.net.URLEncoder;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpHeaders;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.StringEntity;\nimport org.apache.http.impl.client.HttpClientBuilder;\nimport org.apache.http.util.EntityUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.test.inner.web.client.Encrypt;\nimport org.yx.common.util.S;\nimport org.yx.log.Log;\n\n/*\n * 通过这个类，可以了解web的通讯。\n * 加解密部分需要些技术功底才好看明白。\n * 好消息是：现在web应用一般都使用https了，就可以不用加解密方式了，就可以不看加解密部分\n */\npublic class HttpAesClientTest {\n\n\tprivate String getUrl(String act) {\n\t\tString url = \"http://localhost:8080/rest/\" + act;\n\t\tSystem.out.println(\"本次请求的url: \"+url);\n\t\treturn url;\n\t}\n\n\n\t//加密传输的例子。展示了使用表单以及不使用表单两种方式\n\t@Test\n\tpublic void aes_base64() throws Exception {\n\t\tString charset = \"UTF-8\";\n\t\tHttpClient client = HttpClientBuilder.create().build();\n\t\tHttpResponse resp = login(client);\n\t\tString key_str = resp.getFirstHeader(\"skey\").getValue();\n\t\tLog.get(\"login\").info(\"key:{}\", key_str);\n\t\tbyte[] key = Base64.getMimeDecoder().decode(key_str);\n\t\tString act = \"aes_base64\";\n\t\tHttpPost post = new HttpPost(getUrl(act));\n\t\tMap<String, Object> json = new HashMap<>();\n\t\tjson.put(\"echo\", \"你好!!!\");\n\t\tjson.put(\"names\", Arrays.asList(\"小明\", \"小张\"));\n\t\tbyte[] conts = Encrypt.encrypt(S.json().toJson(json).getBytes(charset), key);\n\t\tString req = Base64.getEncoder().encodeToString(conts);\n\t\tSystem.out.println(\"req:\" + req);\n\t\tpost.setHeader(HttpHeaders.CONTENT_TYPE, \"application/x-www-form-urlencoded\");\n\t\tStringEntity se = new StringEntity(\"data=\" + URLEncoder.encode(req, \"ASCII\"), charset);\n\t\tpost.setEntity(se);\n\t\tresp = client.execute(post);\n\t\tString line = resp.getStatusLine().toString();\n\t\tLog.get( \"aes_base64\").info(line);\n\t\tAssert.assertEquals(\"HTTP/1.1 200 OK\", line);\n\t\tHttpEntity resEntity = resp.getEntity();\n\t\tString raw = EntityUtils.toString(resEntity);\n\t\tLog.get(\"aes\").info(\"raw resp:{}\", raw);\n\t\tbyte[] contentBytes = Base64.getMimeDecoder().decode(raw);\n\t\tString ret = new String(Encrypt.decrypt(contentBytes, key), charset);\n\t\tLog.get( \"aes_base64\").info(\"服务器返回：\" + ret);\n\t\tAssert.assertEquals(\"[\\\"你好!!! 小明\\\",\\\"你好!!! 小张\\\"]\", ret);\n\n\t\t/*\n\t\t * 非表单MIME的方式提交,去掉application/x-www-form-urlencoded，内容也不要URLEncoder编码\n\t\t */\n\t\tpost = new HttpPost(getUrl(act));\n\t\tSystem.out.println(\"req:\" + req);\n\t\tse = new StringEntity(\"data=\" + req, charset);\n\t\tpost.setEntity(se);\n\t\tresp = client.execute(post);\n\t\tline = resp.getStatusLine().toString();\n\t\tLog.get( \"aes_base64\").info(line);\n\t\tAssert.assertEquals(\"HTTP/1.1 200 OK\", line);\n\t\tresEntity = resp.getEntity();\n\t\traw = EntityUtils.toString(resEntity);\n\t\tLog.get(\"aes\").info(\"raw resp:{}\", raw);\n\t\tcontentBytes = Base64.getMimeDecoder().decode(raw);\n\t\tret = new String(Encrypt.decrypt(contentBytes, key), charset);\n\t\tLog.get( \"aes_base64\").info(\"服务器返回：\" + ret);\n\t\tAssert.assertEquals(\"[\\\"你好!!! 小明\\\",\\\"你好!!! 小张\\\"]\", ret);\n\n\t\tpost = new HttpPost(getUrl(\"bizError\"));\n\t\tresp = client.execute(post);\n\t\tAssert.assertEquals(550, resp.getStatusLine().getStatusCode());\n\t\tresEntity = resp.getEntity();\n\t\tString errMsg = EntityUtils.toString(resEntity);\n\t\tLog.get(\"aes\").info(\"raw resp:{}\", errMsg);\n\t\tAssert.assertEquals(\"{\\\"code\\\":\\\"12345\\\",\\\"message\\\":\\\"业务异常\\\"}\", errMsg);\n\t}\n\n\t//测试加密传输并且签名\n\t@Test\n\tpublic void aes_sign() throws Exception {\n\t\tString charset = \"UTF-8\";\n\t\tString act = \"aes_sign\";\n\t\t\n\t\t//登陆，并获取加密用的key[]\n\t\tHttpClient client = HttpClientBuilder.create().build();\n\t\tHttpResponse resp = login(client);\n\t\tString logined = EntityUtils.toString(resp.getEntity());\n\t\tSystem.out.println(\"logined:\"+logined);\n\t\tString key_str = resp.getFirstHeader(\"skey\").getValue();\n\t\tLog.get(\"login\").info(\"key:{}\", key_str);\n\t\tbyte[] key = Base64.getMimeDecoder().decode(key_str);\n\t\t\n\t\t\n\t\t//加密数据\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"name\", \"小明\");\n\t\tbyte[] conts = Encrypt.encrypt(S.json().toJson(map).getBytes(charset), key);//用AES加密\n\t\tString req = Base64.getEncoder().encodeToString(conts);//变成base64\n\t\tStringEntity se = new StringEntity(req, charset);\n\t\tLog.get(\"aes_sign\").info(\"req:\" + req);\n\t\t\n\t\t//生成sign，这个是需要sign的接口特有的\n\t\tString sign = Encrypt.sign(S.json().toJson(map).getBytes(charset));\n\t\tSystem.out.println(\"sign:\" + sign);\n\t\t\n\t\tHttpPost post = new HttpPost(getUrl(act) + \"?__sign=\" + sign);\n\t\tpost.setEntity(se);\n\t\tresp = client.execute(post);\n\t\tString line = resp.getStatusLine().toString();\n\t\tLog.get( \"aes_sign\").info(line);\n\n\t\tHttpEntity resEntity = resp.getEntity();\n\t\tString raw = EntityUtils.toString(resEntity);\n\t\tLog.get(\"aes\").info(\"raw resp:{}\", raw);\n\t\tbyte[] contentBytes = Base64.getMimeDecoder().decode(raw);\n\t\tString ret = new String(Encrypt.decrypt(contentBytes, key), charset);\n\t\tLog.get( \"aes_base64\").info(\"服务器返回：\" + ret);\n\t\tAssert.assertEquals(\"hello 小明\", ret);\n\t}\n\n\n\tprivate HttpResponse login(HttpClient client) throws Exception {\n\t\tHttpGet post = new HttpGet(\"http://localhost:8080/login?username=admin&password=123456&code=9999\");\n\t\tHttpResponse resp = client.execute(post);\n\t\tString line = resp.getStatusLine().toString();\n\t\tAssert.assertTrue(line, resp.getStatusLine().getStatusCode() == 200);\n\t\treturn resp;\n\t}\n\n\t\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/client/HttpPlainClientTest.java",
    "content": "package org.test.client;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\n\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.StringEntity;\nimport org.apache.http.entity.mime.MultipartEntity;\nimport org.apache.http.entity.mime.content.FileBody;\nimport org.apache.http.entity.mime.content.StringBody;\nimport org.apache.http.impl.client.HttpClientBuilder;\nimport org.apache.http.util.EntityUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.test.inner.po.DemoUser;\nimport org.test.inner.web.client.Encrypt;\nimport org.yx.common.util.S;\nimport org.yx.log.Log;\n\n/*\n * 通过这个类，可以了解web的通讯。\n */\npublic class HttpPlainClientTest {\n\n\tprivate String getUrl(String act) {\n\t\treturn \"http://localhost:8080/rest/\" + act;\n\t}\n\n\tprivate String getUploadUrl(String act) {\n\t\treturn \"http://localhost:8080/upload/\" + act;\n\t}\n\n\tprivate HttpResponse login(HttpClient client) throws Exception {\n\t\tHttpGet post = new HttpGet(\"http://localhost:8080/login?username=admin&password=123456&code=9999\");\n\t\tHttpResponse resp = client.execute(post);\n\t\tString line = resp.getStatusLine().toString();\n\t\tAssert.assertTrue(line, resp.getStatusLine().getStatusCode() == 200);\n\t\treturn resp;\n\t}\n\n\tRandom r = new Random();\n\t\n\t@Test\n\tpublic void plain() throws IOException {\n\t\tString charset = \"GBK\";\n\t\tHttpClient client = HttpClientBuilder.create().build();\n\t\tString act = \"echo\";\n\t\tHttpPost post = new HttpPost(getUrl(act));\n\t\tMap<String, Object> json = new HashMap<>();\n\t\tjson.put(\"echo\", \"你好!!!\");\n\t\tjson.put(\"names\", Arrays.asList(\"小明\", \"小张\"));\n\t\tStringEntity se = new StringEntity(S.json().toJson(json), charset);\n\t\tpost.setEntity(se);\n\t\tHttpResponse resp = client.execute(post);\n\t\tString line = resp.getStatusLine().toString();\n\t\tAssert.assertEquals(\"HTTP/1.1 200 OK\", line);\n\t\tHttpEntity resEntity = resp.getEntity();\n\t\tString ret = EntityUtils.toString(resEntity, charset);\n\t\tAssert.assertEquals(\"[\\\"你好!!! 小明\\\",\\\"你好!!! 小张\\\"]\", ret);\n\t}\n\n\t//测试需要登录的情况\n\t@Test\n\tpublic void login_sign() throws Exception {\n\t\tString charset = \"UTF-8\";\n\t\tHttpClient client = HttpClientBuilder.create().build();\n\t\tlogin(client);\n\t\tString act = \"plain_sign\";\n\t\tMap<String, Object> json = new HashMap<>();\n\t\tjson.put(\"name\", \"小明\");\n\t\tString req = S.json().toJson(json);\n\t\tString sign = Encrypt.sign(req.getBytes(charset));\n\t\tHttpPost post = new HttpPost(getUrl(act) + \"?__sign=\" + sign);\n\t\tStringEntity se = new StringEntity(req,charset);\n\t\tpost.setEntity(se);\n\t\tHttpResponse resp = client.execute(post);\n\t\t\n\t\t//以下验证请求是否正确\n\t\tString line = resp.getStatusLine().toString();\n\t\tAssert.assertEquals(\"HTTP/1.1 200 OK\", line);\n\t\tHttpEntity resEntity = resp.getEntity();\n\t\tString ret = EntityUtils.toString(resEntity,charset);\n\t\tAssert.assertEquals(\"hello 小明，来自admin的问候\", ret);\n\t}\n\n\t@Test\n\tpublic void base64() throws IOException {\n\t\tString charset = \"UTF-8\";\n\t\tHttpClient client = HttpClientBuilder.create().build();\n\t\tString act = \"base64\";\n\t\tHttpPost post = new HttpPost(getUrl(act));\n\t\tMap<String, Object> json = new HashMap<>();\n\t\tjson.put(\"echo\", \"你好!!!\");\n\t\tjson.put(\"names\", Arrays.asList(\"小明\", \"小张\"));\n\t\tString req = Base64.getEncoder().encodeToString(S.json().toJson(json).replace(\"\\\"names\\\"\", \"names\").getBytes(charset));\n\t\tSystem.out.println(\"req:\" + req);\n\t\tStringEntity se = new StringEntity(req, charset);\n\t\tpost.setEntity(se);\n\t\tHttpResponse resp = client.execute(post);\n\t\tString line = resp.getStatusLine().toString();\n\t\tAssert.assertEquals(\"HTTP/1.1 200 OK\", line);\n\t\tHttpEntity resEntity = resp.getEntity();\n\t\tString ret = new String(Base64.getMimeDecoder().decode(EntityUtils.toString(resEntity)), charset);\n\t\tAssert.assertEquals(\"[\\\"你好!!! 小明\\\",\\\"你好!!! 小张\\\"]\", ret);\n\t}\n\n\t\n\n\n\t@Test\n\tpublic void upload() throws IOException {\n\t\tString charset = \"UTF-8\";\n\t\tHttpClient client = HttpClientBuilder.create().build();\n\t\tString act = \"upload\";\n\t\tHttpPost post = new HttpPost(getUploadUrl(act));\n\t\tMap<String, Object> json = new HashMap<>();\n\t\tjson.put(\"name\", \"张三\");\n\t\tjson.put(\"age\", 23);\n\t\tString req = Base64.getEncoder().encodeToString(S.json().toJson(json).getBytes(charset));\n\t\tSystem.out.println(\"req:\" + req);\n\n\t\tMultipartEntity reqEntity = new MultipartEntity();\n\t\treqEntity.addPart(\"Api\", StringBody.create(\"common\", \"text/plain\", Charset.forName(charset)));\n\t\treqEntity.addPart(\"param\", StringBody.create(req, \"text/plain\", Charset.forName(charset)));\n\t\treqEntity.addPart(\"img\", new FileBody(new File(\"test.sql\")));\n\n\t\tpost.setEntity(reqEntity);\n\t\tHttpResponse resp = client.execute(post);\n\t\tString line = resp.getStatusLine().toString();\n\t\tAssert.assertEquals(\"HTTP/1.1 200 OK\", line);\n\t\tHttpEntity resEntity = resp.getEntity();\n\t\tLog.get(\"upload\").info(EntityUtils.toString(resEntity, charset));\n\t}\n\n\t\n\n\t@Test\n\tpublic void db_insert() throws IOException {\n\t\tString charset = \"UTF-8\";\n\t\tHttpClient client = HttpClientBuilder.create().build();\n\t\tString act = \"add\";\n\t\tHttpPost post = new HttpPost(getUrl(act));\n\t\tList<DemoUser> list = new ArrayList<>();\n\n\t\tfor (int i = 0; i < 10; i++) {\n\t\t\tDemoUser obj = new DemoUser();\n\t\t\tobj.setAge(r.nextInt(100));\n\t\t\tobj.setName(\"名字\" + r.nextInt());\n\t\t\tobj.setId(r.nextLong());\n\t\t\tlist.add(obj);\n\t\t}\n\t\tMap<String, Object> json = new HashMap<>();\n\t\tjson.put(\"users\", list);\n\t\tStringEntity se = new StringEntity(S.json().toJson(json), charset);\n\t\tpost.setEntity(se);\n\t\tHttpResponse resp = client.execute(post);\n\t\tString line = resp.getStatusLine().toString();\n\t\tSystem.out.println(line);\n\t\tHttpEntity resEntity = resp.getEntity();\n\t\tString ret = EntityUtils.toString(resEntity, charset);\n\t\tSystem.out.println(ret);\n\t\tAssert.assertEquals(list.size() + \"\", ret);\n\t}\n\n\t@Test\n\tpublic void db_insert_query() throws IOException {\n\t\tString charset = \"UTF-8\";\n\t\tHttpClient client = HttpClientBuilder.create().build();\n\t\tString act = \"addAndGet\";\n\t\tHttpPost post = new HttpPost(getUrl(act));\n\t\tList<DemoUser> list = new ArrayList<>();\n\n\t\tfor (int i = 0; i < 10; i++) {\n\t\t\tDemoUser obj = new DemoUser();\n\t\t\tobj.setAge(r.nextInt(100));\n\t\t\tobj.setName(\"名字\" + r.nextInt());\n\t\t\tobj.setId(r.nextLong());\n\t\t\tlist.add(obj);\n\t\t}\n\t\tMap<String, Object> json = new HashMap<>();\n\t\tjson.put(\"users\", list);\n\t\tStringEntity se = new StringEntity(S.json().toJson(json), charset);\n\t\tpost.setEntity(se);\n\t\tHttpResponse resp = client.execute(post);\n\t\tString line = resp.getStatusLine().toString();\n\t\tSystem.out.println(line);\n\t\tHttpEntity resEntity = resp.getEntity();\n\t\tString ret = EntityUtils.toString(resEntity, charset);\n\t\tLog.get(\"db_insert_query\").info(\"返回结果：\" + ret);\n\t\tSystem.out.println(ret);\n\t}\n\t\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/client/HttpPressTest.java",
    "content": "/**\n * Copyright (C) 2016 - 2017 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.test.client;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.StringEntity;\nimport org.apache.http.impl.client.HttpClientBuilder;\nimport org.apache.http.util.EntityUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.yx.common.util.S;\n\n\npublic class HttpPressTest {\n\t@Test\n\tpublic void test() throws IOException, InterruptedException {\n\t\tString charset = \"utf-8\";\n\t\tHttpClient client = HttpClientBuilder.create().setMaxConnTotal(5000).setMaxConnPerRoute(1000).build();\n\t\tExecutorService executor=Executors.newFixedThreadPool(500);\n\t\tHttpPost post = new HttpPost(\"http://localhost:8080/rest/echo\");\n\t\tMap<String, Object> json = new HashMap<>();\n\t\tjson.put(\"echo\", \"你好!!!\");\n\t\tjson.put(\"names\", Arrays.asList(\"小明\", \"小张\"));\n\t\tStringEntity se = new StringEntity(S.json().toJson(json), charset);\n\t\tpost.setEntity(se);\n\t\tSystem.out.println(\"开始压测，请耐心等待10秒左右。。。\");\n\t\tlong begin=System.currentTimeMillis();\n\t\tint count=100000;\n\t\tAtomicLong totalRT=new AtomicLong();\n\t\tAtomicLong success=new AtomicLong();\n\t\tfor(int i=0;i<count;i++){\n\t\t\texecutor.execute(()->{\n\t\t\t\ttry {\n\t\t\t\t\tlong b2=System.currentTimeMillis();\n\t\t\t\t\tHttpResponse resp = client.execute(post);\n\t\t\t\t\tHttpEntity resEntity = resp.getEntity();\n\t\t\t\t\ttotalRT.addAndGet(System.currentTimeMillis()-b2);\n\t\t\t\t\tString ret = EntityUtils.toString(resEntity, charset);\n\t\t\t\t\tAssert.assertEquals(\"[\\\"你好!!! 小明\\\",\\\"你好!!! 小张\\\"]\", ret);\n\t\t\t\t\tsuccess.incrementAndGet();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t}\n\t\texecutor.shutdown();\n\t\texecutor.awaitTermination(1, TimeUnit.DAYS);\n\t\tlong time=System.currentTimeMillis()-begin;\n\t\tAssert.assertEquals(count, success.get());\n\t\tSystem.out.println(count+\"次http请求总耗时:\"+time+\"ms,平均每秒请求数:\"+(count*1000d/time));\n\t\tSystem.out.println(\"平均每个请求耗时：\"+totalRT.get()/count+\"ms\");\n\t}\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/client/RpcPressTest.java",
    "content": "package org.test.client;\n\nimport java.util.Arrays;\nimport java.util.Random;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.yx.log.LogLevel;\nimport org.yx.log.Loggers;\nimport org.yx.main.StartConstants;\nimport org.yx.main.SumkServer;\nimport org.yx.rpc.client.LockHolder;\nimport org.yx.rpc.client.Rpc;\nimport org.yx.util.SumkDate;\n\npublic class RpcPressTest {\n\n\t@Before\n\tpublic void before(){\n\t\tSumkServer.start(RpcPressTest.class,Arrays.asList(StartConstants.NOHTTP,StartConstants.NOSOA));\n\t\tLoggers.setDefaultLevel(LogLevel.ERROR);//这个只能修改默认级别的，如果有具体设置了日志级别，它的优先级比这个高\n\t\tRpc.init();\n\t}\n\t\n\t@After\n\tpublic void after(){\n\t\tAssert.assertEquals(0, LockHolder.lockSize());\n\t\tSystem.out.println(\"锁状态正确！\");\n\t}\n\t\n\tRandom r=new Random();\n\t\n\t@Test\n\tpublic void test() throws InterruptedException {\n\t\tSystem.out.println(\"开始压测，请耐心等待30秒左右。。。\");\n\t\tfinal int count=50_0000;\n\t\tfinal int threadCount=50;\n\t\tAtomicInteger failCount=new AtomicInteger();\n\t\tCountDownLatch down=new CountDownLatch(count);\n\t\tRpc.call(\"a.b.repeat\", \"预热\");\n\t\tchar[] cs=new char[1200];\n\t\tArrays.fill(cs, 't');\n\t\tArrays.fill(cs, 100,200,'好');\n\t\tcs[5]='啊';\n\t\tcs[45]='1';\n\t\tcs[59]='o';\n\t\tcs[115]='是';\n\t\tcs[800]='是';\n\t\tString pre=new String(cs);\n\t\tlong begin=System.currentTimeMillis();\n\t\tAtomicLong totalRT=new AtomicLong();\n\t\tfor(int i=0;i<threadCount;i++){\n\t\t\tnew Thread(){\n\t\t\t\t@Override\n\t\t\t\tpublic void run(){\n\t\t\t\t\tlong t=0;\n\t\t\t\t\tfor(int i=0;i<count/threadCount;i++){\n\t\t\t\t\t\t//收发1k左右的数据量\n\t\t\t\t\t\tString msg=pre+i;\n\t\t\t\t\t\tlong b2=System.currentTimeMillis();\n\t\t\t\t\t\tString msg2=Rpc.create(\"a.b.repeat\").paramInArray(msg).timeout(30000).execute().getOrException();\n\t\t\t\t\t\tt+=System.currentTimeMillis()-b2;\n\t\t\t\t\t\tif(!(\"\\\"\"+msg+\"\\\"\").equals(msg2)){\n\t\t\t\t\t\t\tfailCount.incrementAndGet();\n\t\t\t\t\t\t\tSystem.out.println(msg);\n\t\t\t\t\t\t\tSystem.out.println(msg2);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdown.countDown();\n\t\t\t\t\t}\n\t\t\t\t\ttotalRT.addAndGet(t);\n\t\t\t\t}\n\t\t\t}.start();\n\t\t}\n\t\twhile(!down.await(5, TimeUnit.SECONDS)){\n\t\t\tSystem.out.println(SumkDate.now()+\" - 剩余请求：\"+down.getCount());\n\t\t}\n\t\tlong time=System.currentTimeMillis()-begin;\n\t\tSystem.out.println(\"耗时：\"+time+\",平均每秒请求数：\"+(count*1000d/time));\n\t\tSystem.out.println(\"平均每个响应耗时：\"+totalRT.get()/count+\"ms\");\n\t\tAssert.assertEquals(0, failCount.get());\n\t}\n\t\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/client/RpcTest.java",
    "content": "package org.test.client;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\n\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.test.inner.po.DemoUser;\nimport org.test.inner.soa.demo.EchoAction;\nimport org.yx.common.util.S;\nimport org.yx.conf.AppInfo;\nimport org.yx.main.StartConstants;\nimport org.yx.main.SumkServer;\nimport org.yx.rpc.client.Rpc;\nimport org.yx.rpc.client.RpcFuture;\n\n// RPC是异步启动的，所以有可能打印启动成功，但实际上还没有\npublic class RpcTest {\n\n\t@BeforeClass\n\tpublic static void before() {\n\t\tSumkServer.start(RpcTest.class,Arrays.asList(StartConstants.NOHTTP,StartConstants.NOSOA));\n\t\tRpc.init();\n\t}\n\n\tpublic static String soaName(String soaName){\n\t\tStringBuilder sb=new StringBuilder();\n\t\tif(AppInfo.appId(null)!=null){\n\t\t\tsb.append(AppInfo.appId(null)).append('.');\n\t\t}\n\t\treturn sb.append(soaName).toString();\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tList<String> names = Arrays.asList(\"游夏\", \"游侠\");\n\t\tString echo = \"how are you\";\n\t\t// ret是json格式\n\t\tString ret = Rpc.call(soaName(\"echo\"), echo, names);\n\t\tSystem.out.println(\"result:\" + ret);\n\t\tAssert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class));\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tMap<String, Object> map = new HashMap<>();\n\t\t\tmap.put(\"echo\", echo);\n\t\t\tmap.put(\"names\", names);\n\t\t\tret = Rpc.callInMap(soaName(\"echo\"), map);\n\t\t\tAssert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class));\n\t\t\tret = Rpc.call(soaName(\"echo\"), echo, names);\n\t\t\tAssert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class));\n\t\t\tSystem.out.println(\"test:\" + ret);\n\t\t}\n\t}\n\n\tRandom r = new Random();\n\n\tprivate List<DemoUser> create() {\n\t\tList<DemoUser> list = new ArrayList<DemoUser>();\n\t\tfor (int i = 0; i < 10; i++) {\n\t\t\tDemoUser obj = new DemoUser();\n\t\t\tobj.setAge(r.nextInt(100));\n\t\t\tobj.setName(\"名字\" + r.nextInt());\n\t\t\tobj.setId(r.nextLong());\n\t\t\tlist.add(obj);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Test\n\tpublic void db_insert() throws IOException {\n\t\tfor (int j = 0; j < 5; j++) {\n\t\t\tList<DemoUser> list = create();\n\t\t\tString ret;\n\t\t\tMap<String, Object> map = new HashMap<>();\n\t\t\tmap.put(\"users\", list);\n\t\t\tret = Rpc.callInMap(soaName(\"add\"), map);\n\t\t\tSystem.out.println(\"返回的信息：\" + ret);\n\t\t\tAssert.assertEquals(list.size() + \"\", ret);\n\n\t\t\tlist = create();\n\t\t\tret = Rpc.call(soaName(\"add\"), list);\n\t\t\tSystem.out.println(\"返回的信息：\" + ret);\n\t\t\tAssert.assertEquals(list.size() + \"\", ret);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void async() {\n\t\tSystem.out.println(\"now:\" + System.currentTimeMillis());\n\t\tList<String> names = Arrays.asList(\"游夏\", \"游侠\");\n\t\tString echo = \"how are you,\";\n\t\t// ret是json格式\n\t\tList<RpcFuture> retList = new ArrayList<>();\n\t\tList<RpcFuture> retList2 = new ArrayList<>();\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tMap<String, Object> map = new HashMap<>();\n\t\t\tmap.put(\"echo\", echo + i);\n\t\t\tmap.put(\"names\", names);\n\t\t\tretList.add(Rpc.callInMapAsync(soaName(\"echo\"), map));\n\t\t\tretList2.add(Rpc.callAsync(soaName(\"echo\"), echo + i, names));\n\t\t}\n\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tSystem.out.println(i + \" 异步\");\n\t\t\tAssert.assertEquals(new EchoAction().echo(echo + i, names),\n\t\t\t\t\tS.json().fromJson(retList.get(i).opt(), List.class));\n\t\t\tAssert.assertEquals(new EchoAction().echo(echo + i, names),\n\t\t\t\t\tS.json().fromJson(retList2.get(i).opt(), List.class));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/dao/DemoUserDao.java",
    "content": "/**\n * Copyright (C) 2016 - 2017 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.test.inner.dao;\n\nimport java.text.ParseException;\n\nimport org.test.inner.po.DemoUser;\n\npublic interface DemoUserDao {\n\n\tLong insert(DemoUser obj);\n\n\t// 更新部分字段\n\tvoid updatePart(DemoUser obj);\n\n\t// 更新全部字段\n\tvoid fullUpate(long id);\n\n\tvoid softDelete(long id);\n\n\tDemoUser query(long id);\n\n\tvoid select() throws ParseException;\n\n}"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/dao/DemoUserDaoImpl.java",
    "content": "package org.test.inner.dao;\n\nimport java.text.ParseException;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Random;\n\nimport org.junit.Assert;\nimport org.test.inner.po.DemoUser;\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.db.Box;\nimport org.yx.common.util.SBuilder;\nimport org.yx.db.DB;\nimport org.yx.db.sql.Select;\nimport org.yx.db.visit.MapResultHandler;\n\n\n@Bean\npublic class DemoUserDaoImpl implements DemoUserDao {\n\n\tpublic Random r = new Random();\n\n\t@Override\n\t@Box\n\tpublic Long insert(DemoUser obj) {\n\t\tif (obj == null) {\n\t\t\tobj = new DemoUser();\n\t\t\tobj.setAge(r.nextInt(100));\n\t\t\tobj.setName(\"名字\" + r.nextInt());\n\t\t\tobj.setLastUpdate(new Date());\n\t\t}\n\t\tSystem.out.println(\"插入：\" + DB.insert(obj).execute() + \"条，id=\" + obj.getId());\n\t\treturn obj.getId();\n\t}\n\n\t// 更新部分字段\n\t@Override\n\t@Box\n\tpublic void updatePart(DemoUser obj) {\n\t\tobj.setName(\"名字改为：\" + r.nextInt());\n\t\tDB.update(obj).execute();\n\t}\n\n\t// 更新全部字段\n\t@Override\n\t@Box\n\tpublic void fullUpate(long id) {\n\t\tDemoUser obj = new DemoUser();\n\t\tobj.setId(id);\n\t\tobj.setName(\"全部更新，除名字外都清空\");\n\t\tDB.update(obj).fullUpdate(true).execute();\n\t}\n\n\t@Override\n\t@Box\n\tpublic void softDelete(long id) {\n\t\tDemoUser obj = new DemoUser();\n\t\tobj.setId(id);\n\t\tDB.delete(obj).execute();\n\t}\n\n\t@Override\n\t@Box\n\tpublic DemoUser query(long id) {\n\t\treturn DB.select().tableClass(DemoUser.class).byDatabaseId(id).queryOne();\n\t}\n\n\t@Override\n\t@Box\n\tpublic void select() throws ParseException {\n\t\tDemoUser obj = new DemoUser();\n\t\tobj.setAge(12);\n\t\tobj.setName(\"kkk\");\n\t\tList<Object> list;\n\t\tSystem.out.println(\"查询中用到的所有字段名，都是java的字段名，而不是数据库的字段名\");\n\n\t\tSystem.out.println(\"查询name=kkk and age=12\");\n\t\tlist = DB.select(obj).queryList(); // 查询name=kkk\n\t\tAssert.assertEquals(list.size(), DB.select(obj).count());\n\n\t\tSystem.out.println(\"用map做条件，查询(id=10000 and age =16) or (id=20000)的记录。用map做条件的时候，key的大小写不敏感，但值类型要跟pojo类定义的一致\");\n\n\t\tSelect select = DB.select().tableClass(DemoUser.class)\n\t\t\t\t.addEqual(SBuilder.map(\"id\", 10000).put(\"age\", 16).toMap()).addEqual(SBuilder.map(\"id\", 20000).toMap());\n\t\tlist = select.queryList();\n\t\tAssert.assertEquals(list.size(), select.count());\n\n\t\tSystem.out.println(\"返回结果是List<Map>的例子。查询lastupdate<当前时间的记录，按lastupdate升序排列，并且limit 10,10（相当于每页10条的第二页数据）\");\n\t\tlist = DB.select().tableClass(DemoUser.class).lessThan(\"lastupdate\", new Date()).orderByAsc(\"lastupdate\")\n\t\t\t\t.offset(10).limit(10).resultHandler(MapResultHandler.handler).queryList();\n\t\tSystem.out.println(\"map list:\" + list);\n\n\t\tSystem.out.println(\n\t\t\t\t\"相当于select id demouser where (name=kkk and age=12) or (name=第1个 and age=1) order by lastupdate,age desc limit 0,10\");\n\t\tDB.select(obj).selectColumns(\"id\").addEqual(SBuilder.map(\"name\", \"第1个\").put(\"AGE\", 1).toMap())\n\t\t\t\t.orderByAsc(\"lastupdate\").orderByDesc(\"age\").limit(10).queryList();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/dao/DemoUserMybatisDao.java",
    "content": "package org.test.inner.dao;\n\nimport java.text.ParseException;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Random;\n\nimport org.test.inner.po.DemoUser;\nimport org.yx.annotation.db.Box;\nimport org.yx.common.util.SBuilder;\nimport org.yx.db.DB;\nimport org.yx.db.visit.MapResultHandler;\n/**\n * mybatis类型的数据库操作，在本工程中没有实际用处，只是给大家做参考\n */\npublic class DemoUserMybatisDao {\n\n\tpublic Random r = new Random();\n\n\t@Box\n\tpublic Long insert(DemoUser obj) {\n\t\tif (obj == null) {\n\t\t\tobj = new DemoUser();\n\t\t\tobj.setAge(r.nextInt(100));\n\t\t\tobj.setName(\"名字\" + r.nextInt());\n\t\t\tobj.setLastUpdate(new Date());\n\t\t}\n\t\tSystem.out.println(\"插入：\" + DB.insert(obj).execute() + \"条，id=\" + obj.getId());\n\t\treturn obj.getId();\n\t}\n\n\t// 更新部分字段\n\t@Box\n\tpublic void updatePart(DemoUser obj) {\n\t\tobj.setName(\"名字改为：\" + r.nextInt());\n\t\tDB.update(obj).execute();\n\t}\n\n\t// 更新全部字段\n\t@Box\n\tpublic void fullUpate(long id) {\n\t\tDemoUser obj = new DemoUser();\n\t\tobj.setId(id);\n\t\tobj.setName(\"全部更新，除名字外都清空\");\n\t\tDB.update(obj).fullUpdate(true).execute();\n\t}\n\n\t@Box\n\tpublic void softDelete(long id) {\n\t\tDemoUser obj = new DemoUser();\n\t\tobj.setId(id);\n\t\tDB.delete(obj).execute();\n\t}\n\n\t@Box\n\tpublic DemoUser query(long id) {\n\t\treturn DB.select().tableClass(DemoUser.class).byDatabaseId(id).queryOne();\n\t}\n\n\t@Box\n\tpublic void select() throws ParseException {\n\t\tDemoUser obj = new DemoUser();\n\t\tobj.setAge(12);\n\t\tobj.setName(\"kkk\");\n\t\tList<Object> list;\n\t\tSystem.out.println(\"查询中用到的所有字段名，都是java的字段名，而不是数据库的字段名\");\n\n\t\tSystem.out.println(\"查询name=kkk and age=12\");\n\t\tlist = DB.select(obj).queryList(); // 查询name=kkk\n\n\t\tSystem.out.println(\"用map做条件，查询(id=10000 and age =16) or (id=20000)的记录。用map做条件的时候，key的大小写不敏感，但值类型要跟pojo类定义的一致\");\n\t\tlist = DB.select().tableClass(DemoUser.class).addEqual(SBuilder.map(\"id\", 10000).put(\"age\", 16).toMap())\n\t\t\t\t.addEqual(SBuilder.map(\"id\", 20000).toMap()).queryList();\n\n\t\tSystem.out.println(\"返回结果是List<Map>的例子。查询lastupdate<当前时间的记录，按lastupdate升序排列，并且limit 10,10（相当于每页10条的第二页数据）\");\n\t\tlist = DB.select().tableClass(DemoUser.class).lessThan(\"lastupdate\", new Date()).orderByAsc(\"lastupdate\")\n\t\t\t\t.offset(10).limit(10).resultHandler(MapResultHandler.handler).queryList();\n\t\tSystem.out.println(\"map list:\" + list);\n\n\t\tSystem.out.println(\n\t\t\t\t\"相当于select id demouser where (name=kkk and age=12) or (name=第1个 and age=1) order by lastupdate,age desc limit 0,10\");\n\t\tDB.select(obj).selectColumns(\"id\").addEqual(SBuilder.map(\"name\", \"第1个\").put(\"AGE\", 1).toMap())\n\t\t\t\t.orderByAsc(\"lastupdate\").orderByDesc(\"age\").limit(10).queryList();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/dao/LocalSqlDao.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.test.inner.dao;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.db.Box;\nimport org.yx.common.util.SBuilder;\nimport org.yx.db.SDB;\n@Bean\npublic class LocalSqlDao {\n\t\n\t@Box\n\tpublic int insertBatch(Map<String,Object> map){\n\t\treturn SDB.execute(\"demo.insertBatch\", map);\n\t}\n\t\n\t@Box\n\tpublic int insert(String name,long id,int age){\n\t\tMap<String,Object> map=SBuilder.map(\"id\",id).put(\"name\", name).put(\"age\", age).toMap();\n\t\treturn SDB.execute(\"demo.insert\", map);\n\t}\n\t\n\t@Box\n\tpublic int update(String name,long id,int age,Date lastUpdate){\n\t\tMap<String, Object> map=SBuilder.map(\"name\",name)\n\t\t\t\t.put(\"id\", id)\n\t\t\t\t.put(\"age\", age)\n\t\t\t\t.put(\"lastUpdate\", lastUpdate)\n\t\t\t\t.toMap();\n\t\treturn SDB.execute(\"demo.update\", map);\n\t}\n\t\n\t@Box\n\tpublic Map<String, Object> select(Long id){\n\t\treturn SDB.queryOne(\"demo.select\",SBuilder.map(\"id\", id).put(\"table\", \"demo_user\").toMap());\n\t}\n\t\n\t@Box\n\tpublic Map<String, Object> select(Map<String, Object> param){\n\t\tMap<String, Object> map=new HashMap<>(param);\n\t\tmap.put(\"table\", \"demo_user\");\n\t\treturn SDB.queryOne(\"demo.select\",map);\n\t}\n\t\n\t@Box\n\tpublic List<Map<String, Object>> selectByIds(Map<String, Object> param){\n\t\treturn SDB.list(\"demo.selectByIds\",param);\n\t}\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/dao/MultikeyDao.java",
    "content": "package org.test.inner.dao;\n\nimport java.util.Random;\n\nimport org.test.inner.po.Multikey;\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.db.Box;\nimport org.yx.db.DB;\nimport org.yx.util.UUIDSeed;\n\n@Bean\npublic class MultikeyDao {\n\tRandom r = new Random();\n\n\t@Box\n\tpublic Multikey insert(Multikey obj) {\n\t\tif (obj == null) {\n\t\t\tobj = create(\"名字\" + r.nextInt(), r.nextInt(100));\n\t\t}\n\t\tSystem.out.println(\"插入：\" + DB.insert(obj).execute() + \"条，id1=\" + obj.getId1());\n\t\treturn obj;\n\t}\n\n\t@Box\n\tpublic void updatePart(Multikey obj) {\n\t\tobj.setName(\"名字改为：\" + r.nextInt());\n\t\tDB.update(obj).execute();\n\t}\n\n\t@Box\n\tpublic void fullUpate(String id1, String id2) {\n\t\tMultikey obj = new Multikey();\n\t\tobj.setId1(id1).setId2(id2);\n\t\tobj.setName(\"全部更新，除名字外都清空\");\n\t\tDB.update(obj).fullUpdate(true).execute();\n\t}\n\n\t@Box\n\tpublic int delete(Multikey obj) {\n\t\treturn DB.delete(obj).execute();\n\t}\n\n\t@Box\n\tpublic Multikey query(String id1, String id2) {\n\t\treturn DB.select(new Multikey().setId1(id1).setId2(id2)).queryOne();\n\t}\n\n\tprivate Multikey create(String name, int age) {\n\t\tMultikey obj = new Multikey();\n\t\tobj.setId1(UUIDSeed.seq());\n\t\tobj.setId2(UUIDSeed.seq());\n\t\tobj.setAge(age);\n\t\tobj.setName(name);\n\t\treturn obj;\n\t}\n\n\t@Box\n\tpublic void incrAge(Multikey obj, int age) {\n\t\tDB.update(obj).incrNum(\"age\", age).execute();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/po/DemoUser.java",
    "content": "package org.test.inner.po;\n\nimport java.util.Date;\n\nimport org.yx.annotation.db.Column;\nimport org.yx.annotation.db.SoftDelete;\nimport org.yx.annotation.db.Table;\nimport org.yx.db.enums.ColumnType;\nimport org.yx.util.SumkDate;\n\n@Table\n@SoftDelete(value = \"enable\", type = Byte.class)\npublic class DemoUser {\n\n\t@Column(type = ColumnType.ID_BOTH)\n\tprivate Long id;\n\tprivate String name;\n\tprivate Integer age;\n\tprivate Date lastUpdate;\n\n\tpublic Date getLastUpdate() {\n\t\treturn lastUpdate;\n\t}\n\n\tpublic void setLastUpdate(Date lastUpdate) {\n\t\tthis.lastUpdate = lastUpdate;\n\t}\n\n\tpublic Long getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(Long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic Integer getAge() {\n\t\treturn age;\n\t}\n\n\tpublic void setAge(Integer age) {\n\t\tthis.age = age;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DemoUser [id=\" + id + \", name=\" + name + \", age=\" + age + \", lastUpdate=\"\n\t\t\t\t+ (lastUpdate==null ? \"null\" : SumkDate.of(lastUpdate)).toString() + \"]\";\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((age == null) ? 0 : age.hashCode());\n\t\tresult = prime * result + ((id == null) ? 0 : id.hashCode());\n\t\tresult = prime * result + ((name == null) ? 0 : name.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tDemoUser other = (DemoUser) obj;\n\t\tif (age == null) {\n\t\t\tif (other.age != null)\n\t\t\t\treturn false;\n\t\t} else if (!age.equals(other.age))\n\t\t\treturn false;\n\t\tif (id == null) {\n\t\t\tif (other.id != null)\n\t\t\t\treturn false;\n\t\t} else if (!id.equals(other.id))\n\t\t\treturn false;\n\t\tif (name == null) {\n\t\t\tif (other.name != null)\n\t\t\t\treturn false;\n\t\t} else if (!name.equals(other.name))\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/po/Multikey.java",
    "content": "package org.test.inner.po;\n\nimport org.yx.annotation.db.Column;\nimport org.yx.annotation.db.Table;\nimport org.yx.db.enums.ColumnType;\n\n@Table\npublic class Multikey {\n\n\t@Column(type = ColumnType.ID_BOTH, order = 1)\n\tprivate String id1;\n\t@Column(type = ColumnType.ID_BOTH, order = 2)\n\tprivate String id2;\n\tprivate String name;\n\tprivate Integer age;\n\n\tpublic String getId1() {\n\t\treturn id1;\n\t}\n\n\tpublic Multikey setId1(String id1) {\n\t\tthis.id1 = id1;\n\t\treturn this;\n\t}\n\n\tpublic String getId2() {\n\t\treturn id2;\n\t}\n\n\tpublic Multikey setId2(String id2) {\n\t\tthis.id2 = id2;\n\t\treturn this;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic Multikey setName(String name) {\n\t\tthis.name = name;\n\t\treturn this;\n\t}\n\n\tpublic Integer getAge() {\n\t\treturn age;\n\t}\n\n\tpublic Multikey setAge(Integer age) {\n\t\tthis.age = age;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Multikey [id1=\" + id1 + \", id2=\" + id2 + \", name=\" + name + \", age=\" + age + \"]\";\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((age == null) ? 0 : age.hashCode());\n\t\tresult = prime * result + ((id1 == null) ? 0 : id1.hashCode());\n\t\tresult = prime * result + ((id2 == null) ? 0 : id2.hashCode());\n\t\tresult = prime * result + ((name == null) ? 0 : name.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj)\n\t\t\treturn true;\n\t\tif (obj == null)\n\t\t\treturn false;\n\t\tif (getClass() != obj.getClass())\n\t\t\treturn false;\n\t\tMultikey other = (Multikey) obj;\n\t\tif (age == null) {\n\t\t\tif (other.age != null)\n\t\t\t\treturn false;\n\t\t} else if (!age.equals(other.age))\n\t\t\treturn false;\n\t\tif (id1 == null) {\n\t\t\tif (other.id1 != null)\n\t\t\t\treturn false;\n\t\t} else if (!id1.equals(other.id1))\n\t\t\treturn false;\n\t\tif (id2 == null) {\n\t\t\tif (other.id2 != null)\n\t\t\t\treturn false;\n\t\t} else if (!id2.equals(other.id2))\n\t\t\treturn false;\n\t\tif (name == null) {\n\t\t\tif (other.name != null)\n\t\t\t\treturn false;\n\t\t} else if (!name.equals(other.name))\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n}"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/soa/demo/EchoAction.java",
    "content": "package org.test.inner.soa.demo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.rpc.Soa;\n\n@Bean\npublic class EchoAction {\n\n\t@Soa(\"a.b.repeat\")\n\tpublic String repeat(String s){\n\t\treturn s;\n\t}\n\t\n\t@Soa\n\tpublic List<String> echo(String echo, List<String> names) {\n\n\t\tList<String> list = new ArrayList<String>();\n\t\tfor (String name : names) {\n\t\t\tlist.add(echo + \" \" + name);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Soa\n\tpublic String hi() {\n\t\treturn \"hello\";\n\t}\n\n\t@Soa\n\tpublic void print() {\n\t\tSystem.out.println(\"print\");\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/soa/server/SOAServer.java",
    "content": "package org.test.inner.soa.server;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\n\nimport org.apache.zookeeper.server.NIOServerCnxnFactory;\nimport org.apache.zookeeper.server.ZooKeeperServer;\nimport org.yx.util.UUIDSeed;\n\npublic class SOAServer {\n\n\tprivate static ZooKeeperServer zk;\n\tprivate static NIOServerCnxnFactory nioFactory;\n\tpublic static void startZKServer() throws IOException, InterruptedException{\n\t\tstartZKServer(500,2000);\n\t}\n\t\n\tpublic static void stopZKServer(){\n\t\tif(zk!=null){\n\t\t\tzk.shutdown();\n\t\t}\n\t}\n\t\n\tpublic static void startZKServer(int tickTime, int minSessionTimeout) throws IOException, InterruptedException{\n\t\tString temp=System.getProperty(\"java.io.tmpdir\");\n\t\tFile f=new File(temp,UUIDSeed.seq());\n\t\tf.mkdir();\n\t\t\n\t\t\n\t\tzk = new ZooKeeperServer(f, f, tickTime);\n        zk.setMinSessionTimeout(minSessionTimeout);\n        nioFactory = new NIOServerCnxnFactory();\n        int maxClientConnections = 0; // 0 means unlimited\n        nioFactory.configure(new InetSocketAddress(\"127.0.0.1\",2181), maxClientConnections);\n        nioFactory.startup(zk);\n\t}\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/web/client/Encrypt.java",
    "content": "/**\n * Copyright (C) 2016 - 2017 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.test.inner.web.client;\n\nimport java.security.MessageDigest;\n\nimport org.yx.common.util.S;\n\n\n\n/**\n * 客户端使用这个类进行加解密、签名\n */\npublic class Encrypt {\n\n\tpublic static byte[] encrypt(byte[] contentBytes, byte[] key) throws Exception {\n\t\treturn S.cipher().encrypt(contentBytes, key);\n\t}\n\n\tpublic static byte[] decrypt(byte[] contentBytes, byte[] key) throws Exception {\n\t\treturn S.cipher().decrypt(contentBytes, key);\n\t}\n\n\t/**\n\t * 对提交数据的明文进行签名\n\t * \n\t * @param data\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static String sign(byte[] data) throws Exception {\n\t\treturn parseByte2HexStr(encryptByte(data));\n\t}\n\n\tpublic static byte[] encryptByte(byte[] data) throws Exception {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tmd.update(data);\n\t\treturn md.digest();\n\t}\n\n\tprivate static String parseByte2HexStr(byte buf[]) {\n\t\tStringBuilder sb = new StringBuilder(32);\n\t\tfor (int i = 0; i < buf.length; i++) {\n\t\t\tString hex = Integer.toHexString(buf[i] & 0xFF);\n\t\t\tif (hex.length() == 1) {\n\t\t\t\thex = '0' + hex;\n\t\t\t}\n\t\t\tsb.append(hex);\n\t\t}\n\t\treturn sb.toString().toLowerCase();\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/web/demo/AesTestServer.java",
    "content": "package org.test.inner.web.demo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.http.Web;\nimport org.yx.http.MessageType;\nimport org.yx.http.WebUtil;\n\n@Bean\npublic class AesTestServer {\n\n\t@Web(value = \"aes_base64\", requestType = MessageType.ENCRYPT_BASE64, responseType = MessageType.ENCRYPT_BASE64)\n\tpublic List<String> aes_base64(String echo, List<String> names) {\n\t\tAssert.assertEquals(\"admin\", WebUtil.getUserObject(DemoSessionObject.class).getUserId());\n\t\tList<String> list = new ArrayList<>();\n\t\tfor (String name : names) {\n\t\t\tlist.add(echo + \" \" + name);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Web(requestType = MessageType.ENCRYPT_BASE64, responseType = MessageType.ENCRYPT_BASE64, sign = true)\n\tpublic String aes_sign(String name) {\n\t\treturn \"hello \" + name;\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/web/demo/DBDemo.java",
    "content": "package org.test.inner.web.demo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.test.inner.po.DemoUser;\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.db.Box;\nimport org.yx.annotation.http.Web;\nimport org.yx.annotation.rpc.Soa;\nimport org.yx.common.util.SeqUtil;\nimport org.yx.db.DB;\nimport org.yx.db.enums.DBType;\nimport org.yx.db.sql.Insert;\nimport org.yx.exception.BizException;\n/*\n * 这个文件用来演示DB操作，其中@Box相当于Spring的@Transactional，用来开启数据库事务。\n * 在Sumk中它是必须的，而且也只需要这个注解。\n * 除此之外，也演示了将@Box、@Soa和@Web混合使用，实际开发中可能会将Controller和Service分开\n */\n@Bean\npublic class DBDemo {\n\n\t@Box\n\tpublic long insert(long id, boolean success) {\n\t\tif (id == 0) {\n\t\t\tid = SeqUtil.next();\n\t\t}\n\t\tDemoUser user = new DemoUser();\n\t\tuser.setAge(2343);\n\t\tuser.setId(id);\n\t\tuser.setName(success ? \"成功插入\" : \"失败记录\");\n\t\tDB.insert(user).execute();\n\t\tif (!success) {\n\t\t\tBizException.throwException(3242, \"故意出错\");\n\t\t}\n\t\treturn id;\n\t}\n\n\t@Web\n\t@Soa\n\t@Box(dbType = DBType.WRITE)\n\tpublic int add(List<DemoUser> users) {\n\t\t//这个是批处理方式\n\t\tInsert insert=DB.insert(DemoUser.class);\n\t\tfor (DemoUser user : users) {\n\t\t\tinsert.insert(user);\n\t\t}\n\t\treturn insert.execute();\n\t}\n\n\t@Web\n\t@Box(dbType = DBType.WRITE) // 实际开发中，一般不需要dbType=DBType.WRITE。只有在插入后要立即查询的情况下，才需要加入这行\n\tpublic List<DemoUser> addAndGet(List<DemoUser> users) {\n\t\tfor (DemoUser user : users) {\n\t\t\tDB.insert(user).execute();\n\t\t}\n\t\tList<DemoUser> list = new ArrayList<>();\n\t\tfor (DemoUser user : users) {\n\t\t\tDemoUser user2 = DB.select().tableClass(DemoUser.class).byDatabaseId(user.getId()).queryOne();\n\t\t\tAssert.assertEquals(DB.select().tableClass(DemoUser.class).byDatabaseId(user.getId()).queryOne(), user2);\n\t\t\tlist.add(user2);\n\t\t}\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/web/demo/DemoSessionObject.java",
    "content": "/**\n * Copyright (C) 2016 - 2017 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.test.inner.web.demo;\n\nimport org.yx.http.user.SessionObject;\n\npublic class DemoSessionObject extends SessionObject {\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/web/demo/MyLoginServlet.java",
    "content": "package org.test.inner.web.demo;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.test.inner.po.DemoUser;\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.db.Box;\nimport org.yx.common.util.SeqUtil;\nimport org.yx.db.DB;\nimport org.yx.http.user.AbstractLoginServlet;\nimport org.yx.http.user.LoginObject;\n\n@Bean\npublic class MyLoginServlet extends AbstractLoginServlet {\n\n\t@Override\n\t@Box\n\tprotected LoginObject login(String token, String user, HttpServletRequest req) {\n\n\t\tString password = req.getParameter(\"password\");\n\t\tString validCode = req.getParameter(\"code\");\n\t\tSystem.out.println(\"login的log：\" + DB.select().tableClass(DemoUser.class).byDatabaseId(log()).queryOne());\n\t\tif (!\"9999\".equals(validCode)) {\n\t\t\treturn LoginObject.fail(\"验证码错误\");\n\t\t}\n\t\tif (\"admin\".equals(user) && \"123456\".equals(password)) {\n\t\t\tDemoSessionObject so = new DemoSessionObject();\n\t\t\tso.setUserId(\"admin\");\n\t\t\treturn LoginObject.success(null, so);\n\t\t}\n\n\t\treturn LoginObject.fail(\"用户名或密码错误\");\n\t}\n\n\tpublic long log() {\n\t\tlong id = SeqUtil.next();\n\t\tDemoUser user = new DemoUser();\n\t\tuser.setAge(2323443);\n\t\tuser.setId(id);\n\t\tuser.setName(\"登陆\");\n\t\tDB.insert(user).execute();\n\t\treturn id;\n\t}\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/inner/web/demo/PlainServer.java",
    "content": "package org.test.inner.web.demo;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.yx.annotation.Bean;\nimport org.yx.annotation.Param;\nimport org.yx.annotation.http.Upload;\nimport org.yx.annotation.http.Web;\nimport org.yx.exception.BizException;\nimport org.yx.http.MessageType;\nimport org.yx.http.WebUtil;\nimport org.yx.http.handler.MultipartItem;\nimport org.yx.util.IOUtil;\n\n\n@Bean\npublic class PlainServer {\n\t\n\t@Web(value = \"echo\")\n\tpublic List<String> echo(String echo, List<String> names) {\n\t\tList<String> list = new ArrayList<>();\n\t\tfor (String name : names) {\n\t\t\tlist.add(echo + \" \" + name);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Web(value = \"base64\", requestType = MessageType.BASE64, responseType = MessageType.BASE64)\n\tpublic List<String> base64(@Param(max = 20) String echo, List<String> names) {\n\t\tList<String> list = new ArrayList<>();\n\t\tfor (String name : names) {\n\t\t\tlist.add(echo + \" \" + name);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Web(value = \"upload\", requestType = MessageType.BASE64)\n\t@Upload\n\tpublic String upload(String name, @Param(required = true) Integer age) throws FileNotFoundException, IOException {\n\t\tAssert.assertEquals(\"张三\", name);\n\t\tAssert.assertEquals(Integer.valueOf(23), age);\n\t\tList<MultipartItem> files=WebUtil.getMultiParts();\n\t\tAssert.assertEquals(2, files.size());\n\t\tMultipartItem f=WebUtil.getPart(\"img\");\n\t\tAssert.assertEquals(\"test.sql\", f.getSubmittedFileName());\n\t\tbyte[] data=IOUtil.readAllBytes(f.getInputStream(),false);\n\t\tbyte[] exp=Files.readAllBytes(new File(\"test.sql\").toPath());\n\t\tAssert.assertArrayEquals(exp, data);\n\t\treturn \"姓名:\"+name+\",年龄:\"+age;\n\t}\n\n\t//本接口要等陆后才能用\n\t@Web(value = \"plain_sign\", sign = true,requireLogin=true)\n\tpublic String plain_sign(String name) {\n\t\treturn \"hello \" + name+\"，来自\"+WebUtil.getUserObject(DemoSessionObject.class).getUserId()+\"的问候\";\n\t}\n\n\t@Web(requestType = MessageType.ENCRYPT_BASE64, responseType = MessageType.ENCRYPT_BASE64)\n\tpublic String bizError() {\n\t\tSystem.out.println(\"req:\" + WebUtil.getHttpRequest());\n\t\tBizException.throwException(12345, \"业务异常\");\n\t\treturn \"\";\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/orm/BaseOrmTest.java",
    "content": "package org.test.orm;\n\nimport java.util.Arrays;\n\nimport org.junit.BeforeClass;\nimport org.test.Main;\nimport org.yx.log.LogLevel;\nimport org.yx.log.Loggers;\nimport org.yx.main.SumkServer;\n\n/**\n * 外键测试\n * \n * @author 游夏\n *\n */\npublic class BaseOrmTest {\n\n\t@BeforeClass\n\tpublic static void beforeClass() {\n\t\tSumkServer.start(Main.class,Arrays.asList(\"nosoa\", \"nohttp\"));\n\t\tLoggers.setDefaultLevel(LogLevel.DEBUG);\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/orm/MultiPrimaryTest.java",
    "content": "package org.test.orm;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.test.inner.dao.MultikeyDao;\nimport org.test.inner.po.Multikey;\nimport org.yx.bean.IOC;\n\npublic class MultiPrimaryTest extends BaseOrmTest{\n\n\tprivate MultikeyDao dao;\n\n\t@Before\n\tpublic void before() {\n\t\tdao = IOC.get(MultikeyDao.class);\n\t}\n\n\t@Test\n\tpublic void crud() {\n\t\tMultikey obj = dao.insert(null);\n\t\tAssert.assertEquals(obj, dao.query(obj.getId1(), obj.getId2()));\n\t\tdao.fullUpate(obj.getId1(), obj.getId2());\n\t\tMultikey real = new Multikey();\n\t\treal.setId1(obj.getId1()).setId2(obj.getId2());\n\t\treal.setName(\"全部更新，除名字外都清空\");\n\t\tAssert.assertEquals(real, dao.query(obj.getId1(), obj.getId2()));\n\n\t\tAssert.assertEquals(1, dao.delete(real));\n\t\tAssert.assertNull(dao.query(obj.getId1(), obj.getId2()));\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/orm/SinglePrimaryTest.java",
    "content": "package org.test.orm;\n\nimport java.io.InputStream;\nimport java.sql.Timestamp;\nimport java.text.ParseException;\nimport java.util.Random;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.test.inner.dao.DemoUserDao;\nimport org.test.inner.po.DemoUser;\nimport org.yx.bean.IOC;\nimport org.yx.main.SumkServer;\nimport org.yx.util.IOUtil;\n\n//单主键的测试\npublic class SinglePrimaryTest extends BaseOrmTest{\n\n\tprivate DemoUserDao dao;\n\n\t@Before\n\tpublic void before() {\n\t\tdao = IOC.get(DemoUserDao.class);\n\t}\n\n\t@Test\n\tpublic void crud() {\n\t\tDemoUser obj = new DemoUser();\n\t\tobj.setAge(new Random().nextInt(100));\n\t\tobj.setName(\"名字\" + new Random().nextInt());\n\t\tobj.setLastUpdate(new Timestamp(System.currentTimeMillis() / 1000 * 1000));// timestamp类型的字段，如果是mysql数据库。用Timestamp或Date类都行，但oracle只能用Timestamp类型\n\t\tdao.insert(obj);\n\t\tAssert.assertEquals(obj, dao.query(obj.getId()));\n\t\tdao.fullUpate(obj.getId());\n\t\tDemoUser real = new DemoUser();\n\t\treal.setId(obj.getId());\n\t\treal.setName(\"全部更新，除名字外都清空\");\n\t\tAssert.assertEquals(real, dao.query(obj.getId()));\n\n\t\tdao.softDelete(obj.getId());\n\t\tAssert.assertNull(dao.query(obj.getId()));\n\t}\n\n\t@Test\n\tpublic void select() throws ParseException {\n\t\tdao.select();\n\t}\n\n\t@Test\n\tpublic void lockFile() throws Exception {\n\t\tInputStream in=SumkServer.class.getClassLoader().getResourceAsStream(\"META-INF/lua_del\");\n\t\tbyte[] bs=IOUtil.readAllBytes(in, true);\n\t\tAssert.assertEquals(95, bs.length);\n\t\tString script=new String(bs);\n\t\tAssert.assertFalse(script.contains(\"\\r\"));\n\t}\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/orm/SqlTest.java",
    "content": "/**\n * Copyright (C) 2016 - 2030 youtongluan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * \t\thttp://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.test.orm;\n\nimport java.io.File;\nimport java.net.URL;\nimport java.nio.file.Files;\nimport java.sql.Timestamp;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Random;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.test.inner.dao.LocalSqlDao;\nimport org.yx.base.date.TimeUtil;\nimport org.yx.bean.IOC;\nimport org.yx.conf.AppInfo;\nimport org.yx.conf.Const;\n\n/*\n * useOldAliasMetadataBehavior=true表示启用别名，额不是数据库定义时的名字，注意连接里这个参数对测试用例的影响\n */\npublic class SqlTest extends BaseOrmTest{\n\n\t@Test\n\tpublic void test() {\n\t\tLocalSqlDao dao=IOC.get(LocalSqlDao.class);\n\t\tString name=\"sdfdsgf\";\n\t\tlong id=new Random().nextLong();\n\t\tint age=103;\n\t\tint ret=dao.insert(name, id, age);\n\t\tAssert.assertEquals(1, ret);\n\t\t\n\t\tMap<String, Object> map=dao.select(id);\n\t\tSystem.out.println(map);\n\t\tAssert.assertEquals(name, map.get(\"name\"));\n\t\tAssert.assertEquals(id, map.get(\"id\"));\n\t\tAssert.assertEquals(age, map.get(\"age\"));\n\t\t\n\t\tMap<String, Object> map2=dao.select(id);\n\t\tAssert.assertEquals(map, map2);\n\t\t\n\t\t//mysql的Timestamp只支持到秒，毫秒会被四舍五入\n\t\tDate lastUpdate=new Date(System.currentTimeMillis()/1000*1000);\n\t\tret=dao.update(name+\"_1\", id, age+1, lastUpdate);\n\t\tAssert.assertEquals(1, ret);\n\t\tmap=dao.select(id);\n\t\tSystem.out.println(map);\n\t\tAssert.assertEquals(name+\"_1\", map.get(\"name\"));\n\t\tAssert.assertEquals(id, map.get(\"id\"));\n\t\tAssert.assertEquals(age+1, map.get(\"age\"));\n\t\tSystem.out.println(map.get(\"lastUpdate\").getClass());\n\t\tAssert.assertEquals(lastUpdate, TimeUtil.toType(map.get(\"lastUpdate\"), Timestamp.class, true));\n\t}\n\t\n\t@Test\n\tpublic void versionTest() throws Exception {\n\t\tURL url=this.getClass().getResource(\"SqlTest.class\");\n\t\tFile f=new File(url.toURI());\n\t\twhile(!f.getName().equals(\"target\")) {\n\t\t\tf=f.getParentFile();\n\t\t}\n\t\tf=f.getParentFile();\n\t\tf=new File(f,\"pom.xml\");\n\t\tSystem.out.println(f);\n\t\tSystem.out.println(Const.sumkVersion());\n\t\tString pom=new String(Files.readAllBytes(f.toPath()),AppInfo.UTF8);\n\t\tAssert.assertEquals(pom.indexOf(\"<version>\"),pom.indexOf(\"<version>\"+Const.sumkVersion()+\"</version>\"));\n\t}\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/token/MapedSqlTokenParserTest.java",
    "content": "package org.test.token;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.yx.db.sql.MapedSql;\nimport org.yx.db.sql.token.MapedSqlTokenParser;\n\nimport java.util.ArrayList;\n\npublic class MapedSqlTokenParserTest {\n\n  @Test\n  public void testParse() {\n    final MapedSql parsed = new MapedSqlTokenParser(\"<\", \">\", null).parse(\"\");\n    Assert.assertNotNull(parsed);\n    Assert.assertEquals(\"\", parsed.getSql());\n    Assert.assertNull(parsed.getEvent());\n    Assert.assertEquals(new ArrayList<>(), parsed.getParamters());\n\n    final MapedSql parsed2 = new MapedSqlTokenParser(\"<\", \">\", null).parse(null);\n    Assert.assertNotNull(parsed2);\n    Assert.assertEquals(\"\", parsed2.getSql());\n    Assert.assertNull(parsed2.getEvent());\n    Assert.assertEquals(new ArrayList<>(), parsed2.getParamters());\n\n    final MapedSql parsed3 = new MapedSqlTokenParser(\"<\", \">\", null).parse(\"text\");\n    Assert.assertNotNull(parsed3);\n    Assert.assertEquals(\"text\", parsed3.getSql());\n    Assert.assertNull(parsed3.getEvent());\n    Assert.assertEquals(new ArrayList<>(), parsed3.getParamters());\n  }\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/java/org/test/token/StringTokenParserTest.java",
    "content": "package org.test.token;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.yx.db.sql.token.StringTokenParser;\n\npublic class StringTokenParserTest {\n\n  @Test\n  public void testParse() {\n    final StringTokenParser objectUnderTest = new StringTokenParser(\"<\", \">\", null);\n    Assert.assertEquals(\"\", objectUnderTest.parse(\"\"));\n    Assert.assertEquals(\"\", objectUnderTest.parse(null));\n    Assert.assertEquals(\"text\", objectUnderTest.parse(\"text\"));\n  }\n\n}\n"
  },
  {
    "path": "sumk-test/src/test/resources/app.properties",
    "content": "#推荐使用UTF-8编码\n#微服务中的zookeeper地址\nsumk.zkurl=127.0.0.1:2181\n\n#used in RPC\nsumk.rpc.port=10086\n\n\n#used in HTTP。zookeeper 3.9.x会默认占用8080，可以在zoo.cfg里配置admin.serverPort来修改zk的端口\nsumk.http.port=8080\n\n#sumk.ioc=org.test\n\n#redis配置\n#s.redis.default=127.0.0.1\n\n#数据库写库的配置\ns.db.sumk.1.type=wr\ns.db.sumk.1.url=jdbc:mysql://120.55.49.121:3306/sumk?characterEncoding=utf-8&useOldAliasMetadataBehavior=true&useSSL=false\ns.db.sumk.1.username=sumk\ns.db.sumk.1.password=sumk123456\n\n#数据库读库的配置\ns.db.sumk.2.type=read\ns.db.sumk.2.url=jdbc:mysql://120.55.49.121:3306/sumk?characterEncoding=utf-8&useOldAliasMetadataBehavior=true&useSSL=false\ns.db.sumk.2.username=read\ns.db.sumk.2.password=sumk123456\n\n"
  },
  {
    "path": "sumk-test/src/test/resources/sql/test.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE sdb SYSTEM \"https://youtongluan.github.io/sumk/sql.dtd\">\n<sdb>\n<sql id=\"test1.queryOne\">\n<!-- ${}转化出来的内容，不能包含表达式 -->\n\tselect * from ${table} where\n\t1=1 and\n\t<if test=\"name | age\"> name like #{name} </if>\n\t<ifnot test=\"name\" falseby=\"empty\"> age=age </ifnot>\n\t<!-- #{@}是列表项的占位符 ,collection支持集合和数组-->\n\t<ifnot test=\"name|id\">\n\t\t<foreach collection=\"id\" item=\"tmp\"  separator=\",\" open=\"(\" close=\")\"> id in #{@}</foreach>\n\t</ifnot>\n\tlimit 10\n</sql>\n\n<sql id=\"selectFirst\">\n<!-- ${}转化出来的内容，不能包含表达式 -->\n\tselect * from ${table} \n\t<![CDATA[\n\t\twhere id>1234\n\t]]>\n\tlimit 1\n</sql>\n<sql id=\"queryWithName\">\n\tselect * from ${table} where\n\t1=1 and\n\t name like #{name} \n\t age=age \n\tlimit 10\n</sql>\n<sql id=\"demouser234314.insert\">\n\tINSERT INTO demo_user (\t\n\t\t\t\tname ,\n\t\t\t\tid ,\n\t\t\t\tage,\n\t\t\t\tenable\n\t\n\t\t)\n\t\tVALUES (#{name},#{id},#{age},1) \n</sql>\n</sdb>"
  },
  {
    "path": "sumk-test/src/test/resources/sql/user/demo.xml",
    "content": "<?xml version=\"1.0\"?>\n<!-- 如果github返回不了，可以从https://gitee.com/youtongluan/sumk/blob/master/sql.dtd下载原文件 -->\n<!DOCTYPE sdb SYSTEM \"https://youtongluan.github.io/sumk/sql.dtd\">\n<sdb namespace=\"demo\">\n<sql id=\"insert\">\n\tinsert into  demo_user (id,name,age,last_update) values (  #{id},#{name},#{age},#{lastUpdate}\t  )\n</sql>\n<sql id=\"insertBatch\">\n\tinsert demo_user (name,age,last_update,id) values\n\t\t<foreach collection=\"list\" item=\"item\" separator=\",\">\n\t\t\t(#{item.name},#{item.age},#{item.lastUpdate},#{item.id})\n\t\t</foreach>\n</sql>\n<sql id=\"update\">\n\tUPDATE  demo_user       \n\t\t   SET  \n\t\t\t\tname  =   #{name}      ,\n\t\t\t\tage  =   #{age}      ,\n\t\t\t\tlast_update=#{lastUpdate}\n\t\tWHERE \n\t\t<if test=\"id\">id = #{id}</if>\n</sql>\n<sql id=\"select\">\n\tselect id,name,age,last_update as lastUpdate from ${table} where 1=1\n\t<if test=\"id\">and id = #{id}</if>\n\t<if test=\"name\" falseby=\"empty\">and name= #{name}</if><!-- 空字符串会被过滤掉，不作为查询条件 -->\n\t<if test=\"age\">and age= #{age}</if>\n\t<if test=\"lastUpdate\">and last_update= #{lastUpdate}</if>\n</sql>\n<sql id=\"selectByIds\">\n\tselect id,name,age,last_update as lastUpdate from demo_user where id in \n\t<foreach collection=\"ids\" item=\"id\" separator=\",\" open=\"(\" close=\")\">#{id}</foreach>\n</sql>\n</sdb>"
  },
  {
    "path": "sumk-test/test.sql",
    "content": "create database sumk;\nuse sumk;\nSET FOREIGN_KEY_CHECKS=0;\n\nDROP TABLE IF EXISTS `demo_user`;\nCREATE TABLE `demo_user` (\n  `id` bigint(20) NOT NULL,\n  `name` varchar(255) DEFAULT NULL,\n  `age` int(11) DEFAULT NULL,\n  `last_update` timestamp NULL DEFAULT NULL,\n  `enable` tinyint(4) DEFAULT '1' COMMENT '1表示有效记录，0表示记录已被删除',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n\nDROP TABLE IF EXISTS `generate`;\nCREATE TABLE `generate` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `name` varchar(255) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n\nDROP TABLE IF EXISTS `multikey`;\nCREATE TABLE `multikey` (\n  `id1` varchar(255) NOT NULL COMMENT '联合主键1',\n  `id2` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL COMMENT '联合主键2',\n  `name` varchar(255) DEFAULT NULL,\n  `age` int(11) DEFAULT NULL,\n  PRIMARY KEY (`id1`,`id2`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n\nDROP TABLE IF EXISTS `odd`;\nCREATE TABLE `odd` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\n  `data` mediumblob,\n  `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=2208669043496333243 DEFAULT CHARSET=utf8;\n\n\nDROP TABLE IF EXISTS `school_fuzhou`;\nCREATE TABLE `school_fuzhou` (\n  `id` bigint(20) NOT NULL,\n  `name` varchar(255) DEFAULT NULL,\n  `lastUpdate` timestamp NULL DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n\nDROP TABLE IF EXISTS `test_table`;\nCREATE TABLE `test_table` (\n  `id` bigint(20) NOT NULL COMMENT 'Long 类型,第1列',\n  `userid` varchar(255) NOT NULL COMMENT 'String 类型，联合索引1,第2列',\n  `enable` bit(1) NOT NULL COMMENT 'boolean 类型，联合索引2,第3列',\n  `height` smallint(6) DEFAULT NULL COMMENT 'Short 类型,第4列',\n  `age` tinyint(4) DEFAULT NULL COMMENT 'byte 类型,第5列',\n  `f` decimal(12,3) DEFAULT NULL COMMENT 'float 类型,第6列',\n  `d` decimal(20,5) DEFAULT NULL COMMENT 'double 类型,第7列',\n  `valid` bit(1) DEFAULT b'1' COMMENT '1表示有效记录，0表示记录已被删除',\n  PRIMARY KEY (`id`),\n  KEY `testtable_index1` (`userid`,`enable`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用来测试表格自动生成';\n\n\nDROP TABLE IF EXISTS `timedemo`;\nCREATE TABLE `timedemo` (\n  `id` bigint(20) NOT NULL,\n  `name` varchar(255) DEFAULT NULL,\n  `lastUpdate` timestamp NULL DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n\nDROP TABLE IF EXISTS `user_detail`;\nCREATE TABLE `user_detail` (\n  `id` bigint(20) NOT NULL,\n  `user_id` bigint(20) NOT NULL,\n  `addr` varchar(255) DEFAULT NULL,\n  `height` int(11) DEFAULT NULL,\n  `valid` char(1) DEFAULT '1' COMMENT '1表示有效记录，0表示记录已被删除',\n  PRIMARY KEY (`id`),\n  KEY `user_detail_index1` (`user_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n"
  }
]